diff options
Diffstat (limited to 'libexec/ftpd')
-rw-r--r-- | libexec/ftpd/Makefile | 39 | ||||
-rw-r--r-- | libexec/ftpd/Makefile.depend | 21 | ||||
-rw-r--r-- | libexec/ftpd/Makefile.depend.options | 5 | ||||
-rw-r--r-- | libexec/ftpd/blacklist.c | 55 | ||||
-rw-r--r-- | libexec/ftpd/blacklist_client.h | 53 | ||||
-rw-r--r-- | libexec/ftpd/config.h | 280 | ||||
-rw-r--r-- | libexec/ftpd/extern.h | 110 | ||||
-rw-r--r-- | libexec/ftpd/ftpchroot.5 | 118 | ||||
-rw-r--r-- | libexec/ftpd/ftpcmd.y | 1806 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.8 | 589 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 3446 | ||||
-rw-r--r-- | libexec/ftpd/ftpusers | 28 | ||||
-rw-r--r-- | libexec/ftpd/logwtmp.c | 70 | ||||
-rw-r--r-- | libexec/ftpd/pathnames.h | 39 | ||||
-rw-r--r-- | libexec/ftpd/popen.c | 193 |
15 files changed, 0 insertions, 6852 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile deleted file mode 100644 index a040fa57f7d7..000000000000 --- a/libexec/ftpd/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -.include <src.opts.mk> - -PACKAGE= ftpd - -CONFS= ftpusers -PROG= ftpd -MAN= ftpd.8 ftpchroot.5 -SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c - -CFLAGS+=-DSETPROCTITLE -DLOGIN_CAP -DVIRTUAL_HOSTING -CFLAGS+=-I${.CURDIR} -YFLAGS= -WARNS?= 2 -WFORMAT=0 - -LIBADD= crypt md util - -.PATH: ${SRCTOP}/bin/ls -SRCS+= ls.c cmp.c print.c util.c -CFLAGS+=-Dmain=ls_main -I${SRCTOP}/bin/ls -LIBADD+= m - -.if ${MK_BLACKLIST_SUPPORT} != "no" -CFLAGS+= -DUSE_BLACKLIST -I${SRCTOP}/contrib/blocklist/include -SRCS+= blacklist.c -LIBADD+= blacklist -LDFLAGS+=-L${LIBBLACKLISTDIR} -.endif - -.if ${MK_INET6_SUPPORT} != "no" -CFLAGS+=-DINET6 -.endif - -.if ${MK_PAM_SUPPORT} != "no" -CFLAGS+=-DUSE_PAM -LIBADD+= pam -.endif - -.include <bsd.prog.mk> diff --git a/libexec/ftpd/Makefile.depend b/libexec/ftpd/Makefile.depend deleted file mode 100644 index b6e94a3cf93c..000000000000 --- a/libexec/ftpd/Makefile.depend +++ /dev/null @@ -1,21 +0,0 @@ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/libc \ - lib/libcompiler_rt \ - lib/libcrypt \ - lib/libmd \ - lib/libutil \ - lib/msun \ - usr.bin/yacc.host \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/libexec/ftpd/Makefile.depend.options b/libexec/ftpd/Makefile.depend.options deleted file mode 100644 index 5f186bb031f2..000000000000 --- a/libexec/ftpd/Makefile.depend.options +++ /dev/null @@ -1,5 +0,0 @@ -# This file is not autogenerated - take care! - -DIRDEPS_OPTIONS= BLACKLIST_SUPPORT PAM_SUPPORT - -.include <dirdeps-options.mk> diff --git a/libexec/ftpd/blacklist.c b/libexec/ftpd/blacklist.c deleted file mode 100644 index 0a45f9369074..000000000000 --- a/libexec/ftpd/blacklist.c +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * Copyright (c) 2016 The FreeBSD Foundation - * - * This software was developed by Kurt Lidl under sponsorship from the - * FreeBSD Foundation. - * - * 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 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 AUTHOR 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 <ctype.h> -#include <stdarg.h> -#include <stdlib.h> -#include <unistd.h> - -#include <blacklist.h> -#include "blacklist_client.h" - -static struct blacklist *blstate; -extern int use_blacklist; - -void -blacklist_init(void) -{ - - if (use_blacklist) - blstate = blacklist_open(); -} - -void -blacklist_notify(int action, int fd, const char *msg) -{ - - if (blstate == NULL) - return; - (void)blacklist_r(blstate, action, fd, msg); -} diff --git a/libexec/ftpd/blacklist_client.h b/libexec/ftpd/blacklist_client.h deleted file mode 100644 index 0b6805dc218e..000000000000 --- a/libexec/ftpd/blacklist_client.h +++ /dev/null @@ -1,53 +0,0 @@ -/*- - * Copyright (c) 2016 The FreeBSD Foundation - * - * This software was developed by Kurt Lidl under sponsorship from the - * FreeBSD Foundation. - * - * 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 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 AUTHOR 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 BLACKLIST_CLIENT_H -#define BLACKLIST_CLIENT_H - -#ifndef BLACKLIST_API_ENUM -enum { - BLACKLIST_AUTH_OK = 0, - BLACKLIST_AUTH_FAIL -}; -#endif - -#ifdef USE_BLACKLIST -void blacklist_init(void); -void blacklist_notify(int, int, const char *); - -#define BLACKLIST_INIT() blacklist_init() -#define BLACKLIST_NOTIFY(x, y, z) blacklist_notify(x, y, z) - -#else - -#define BLACKLIST_INIT() -#define BLACKLIST_NOTIFY(x, y, z) - -#endif - -#endif /* BLACKLIST_CLIENT_H */ diff --git a/libexec/ftpd/config.h b/libexec/ftpd/config.h deleted file mode 100644 index c5ca1f01e10e..000000000000 --- a/libexec/ftpd/config.h +++ /dev/null @@ -1,280 +0,0 @@ - - -/* config.h. Generated automatically by configure. */ -/* config.h.in. Generated automatically from configure.in by autoheader. */ -/* $Id: config.h.in,v 1.15 2001/04/28 07:11:46 lukem Exp $ */ - - -/* Define if the closedir function returns void instead of int. */ -/* #undef CLOSEDIR_VOID */ - -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Define if your Fortran 77 compiler doesn't accept -c and -o together. */ -/* #undef F77_NO_MINUS_C_MINUS_O */ - -/* Define to `long' if <sys/types.h> doesn't define. */ -/* #undef off_t */ - -/* Define to the type of arg1 for select(). */ -/* #undef SELECT_TYPE_ARG1 */ - -/* Define to the type of args 2, 3 and 4 for select(). */ -/* #undef SELECT_TYPE_ARG234 */ - -/* Define to the type of arg5 for select(). */ -/* #undef SELECT_TYPE_ARG5 */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -#define TIME_WITH_SYS_TIME 1 - -/* Define if the closedir function returns void instead of int. */ -/* #undef VOID_CLOSEDIR */ - -/* The number of bytes in a off_t. */ -#define SIZEOF_OFF_T 0 - -/* Define if you have the err function. */ -#define HAVE_ERR 1 - -/* Define if you have the fgetln function. */ -#define HAVE_FGETLN 1 - -/* Define if you have the flock function. */ -#define HAVE_FLOCK 1 - -/* Define if you have the fparseln function. */ -#define HAVE_FPARSELN 1 - -/* Define if you have the fts_open function. */ -#define HAVE_FTS_OPEN 1 - -/* Define if you have the getaddrinfo function. */ -#define HAVE_GETADDRINFO 1 - -/* Define if you have the getgrouplist function. */ -#define HAVE_GETGROUPLIST 1 - -/* Define if you have the getnameinfo function. */ -#define HAVE_GETNAMEINFO 1 - -/* Define if you have the getspnam function. */ -/* #undef HAVE_GETSPNAM */ - -/* Define if you have the getusershell function. */ -#define HAVE_GETUSERSHELL 1 - -/* Define if you have the inet_net_pton function. */ -#define HAVE_INET_NET_PTON 1 - -/* Define if you have the inet_ntop function. */ -#define HAVE_INET_NTOP 1 - -/* Define if you have the inet_pton function. */ -#define HAVE_INET_PTON 1 - -/* Define if you have the lockf function. */ -#define HAVE_LOCKF 1 - -/* Define if you have the mkstemp function. */ -#define HAVE_MKSTEMP 1 - -/* Define if you have the setlogin function. */ -#define HAVE_SETLOGIN 1 - -/* Define if you have the setproctitle function. */ -#define HAVE_SETPROCTITLE 1 - -/* Define if you have the sl_init function. */ -#define HAVE_SL_INIT 1 - -/* Define if you have the snprintf function. */ -#define HAVE_SNPRINTF 1 - -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - -/* Define if you have the strerror function. */ -#define HAVE_STRERROR 1 - -/* Define if you have the strlcat function. */ -#define HAVE_STRLCAT 1 - -/* Define if you have the strlcpy function. */ -#define HAVE_STRLCPY 1 - -/* Define if you have the strmode function. */ -#define HAVE_STRMODE 1 - -/* Define if you have the strsep function. */ -#define HAVE_STRSEP 1 - -/* Define if you have the strtoll function. */ -#define HAVE_STRTOLL 1 - -/* Define if you have the user_from_uid function. */ -#define HAVE_USER_FROM_UID 1 - -/* Define if you have the usleep function. */ -#define HAVE_USLEEP 1 - -/* Define if you have the vfork function. */ -#define HAVE_VFORK 1 - -/* Define if you have the vsyslog function. */ -#define HAVE_VSYSLOG 1 - -/* Define if you have the <arpa/nameser.h> header file. */ -#define HAVE_ARPA_NAMESER_H 1 - -/* Define if you have the <dirent.h> header file. */ -#define HAVE_DIRENT_H 1 - -/* Define if you have the <err.h> header file. */ -#define HAVE_ERR_H 1 - -/* Define if you have the <fts.h> header file. */ -#define HAVE_FTS_H 1 - -/* Define if you have the <libutil.h> header file. */ -#define HAVE_LIBUTIL_H 1 - -/* Define if you have the <ndir.h> header file. */ -/* #undef HAVE_NDIR_H */ - -/* Define if you have the <paths.h> header file. */ -#define HAVE_PATHS_H 1 - -/* Define if you have the <sys/dir.h> header file. */ -#define HAVE_SYS_DIR_H 1 - -/* Define if you have the <sys/ndir.h> header file. */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define if you have the <sys/sysmacros.h> header file. */ -/* #undef HAVE_SYS_SYSMACROS_H */ - -/* Define if you have the <util.h> header file. */ -/* #undef HAVE_UTIL_H */ - -/* Define if you have the crypt library (-lcrypt). */ -#define HAVE_LIBCRYPT 1 - -/* Define if you have the nsl library (-lnsl). */ -/* #undef HAVE_LIBNSL */ - -/* Define if you have the skey library (-lskey). */ -/* #undef HAVE_LIBSKEY */ - -/* Define if you have the socket library (-lsocket). */ -/* #undef HAVE_LIBSOCKET */ - -/* Define if you have the util library (-lutil). */ -#define HAVE_LIBUTIL 1 - -/* Define if your compiler supports `long long' */ -#define HAVE_LONG_LONG 1 - -/* Define if *printf() uses %qd to print `long long' (otherwise uses %lld) */ -#define HAVE_PRINTF_QD 1 - -/* Define if in_port_t exists */ -#define HAVE_IN_PORT_T 1 - -/* Define if struct sockaddr.sa_len exists (implies sockaddr_in.sin_len, etc) */ -#define HAVE_SOCKADDR_SA_LEN 1 - -/* Define if socklen_t exists */ -#define HAVE_SOCKLEN_T 1 - -/* Define if AF_INET6 exists in <sys/socket.h> */ -#define HAVE_AF_INET6 1 - -/* Define if `struct sockaddr_in6' exists in <netinet/in.h> */ -#define HAVE_SOCKADDR_IN6 1 - -/* Define if `struct addrinfo' exists in <netdb.h> */ -#define HAVE_ADDRINFO 1 - -/* - * Define if <netdb.h> contains AI_NUMERICHOST et al. - * Systems which only implement RFC2133 will need this. - */ -#define HAVE_RFC2553_NETDB 1 - -/* Define if `struct direct' has a d_namlen element */ -#define HAVE_D_NAMLEN 1 - -/* Define if struct passwd.pw_expire exists. */ -#define HAVE_PW_EXPIRE 1 - -/* Define if GLOB_BRACE, gl_path and gl_match exist in <glob.h> */ -#define HAVE_WORKING_GLOB 1 - -/* Define if crypt() is declared in <unistd.h> */ -#define HAVE_CRYPT_D 1 - -/* Define if fclose() is declared in <stdio.h> */ -#define HAVE_FCLOSE_D 1 - -/* Define if optarg is declared in <stdlib.h> or <unistd.h> */ -#define HAVE_OPTARG_D 1 - -/* Define if optind is declared in <stdlib.h> or <unistd.h> */ -#define HAVE_OPTIND_D 1 - -/* Define if optreset exists */ -#define HAVE_OPTRESET 1 - -/* Define if pclose() is declared in <stdio.h> */ -#define HAVE_PCLOSE_D 1 - -/* Define if getusershell() is declared in <unistd.h> */ -#define HAVE_GETUSERSHELL_D 1 - -/* Define if `long long' is supported and sizeof(off_t) >= 8 */ -#define HAVE_QUAD_SUPPORT 1 - -/* Define if not using in-built /bin/ls code */ -/* #undef NO_INTERNAL_LS */ - -/* Define if using S/Key */ -/* #undef SKEY */ - -/* - * Define this if compiling with SOCKS (the firewall traversal library). - * Also, you must define connect, getsockname, bind, accept, listen, and - * select to their R-versions. - */ -/* #undef SOCKS */ -/* #undef SOCKS4 */ -/* #undef SOCKS5 */ -/* #undef connect */ -/* #undef getsockname */ -/* #undef bind */ -/* #undef accept */ -/* #undef listen */ -/* #undef select */ -/* #undef dup */ -/* #undef dup2 */ -/* #undef fclose */ -/* #undef gethostbyname */ -/* #undef getpeername */ -/* #undef read */ -/* #undef recv */ -/* #undef recvfrom */ -/* #undef rresvport */ -/* #undef send */ -/* #undef sendto */ -/* #undef shutdown */ -/* #undef write */ - -/* Define if you have the <arpa/ftp.h> header file. */ -#define HAVE_FTP_NAMES 1 diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h deleted file mode 100644 index 047e8573dd09..000000000000 --- a/libexec/ftpd/extern.h +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -#include <sys/types.h> -#include <sys/socket.h> - -void blkfree(char **); -char **copyblk(char **); -void cwd(char *); -void delete(char *); -void dologout(int); -void fatalerror(char *); -void ftpd_logwtmp(char *, char *, struct sockaddr *addr); -int ftpd_pclose(FILE *); -FILE *ftpd_popen(char *, char *); -int get_line(char *, int, FILE *); -void lreply(int, const char *, ...) __printflike(2, 3); -void makedir(char *); -void nack(char *); -void pass(char *); -void passive(void); -void long_passive(char *, int); -void perror_reply(int, char *); -void pwd(void); -void removedir(char *); -void renamecmd(char *, char *); -char *renamefrom(char *); -void reply(int, const char *, ...) __printflike(2, 3); -void retrieve(char *, char *); -void send_file_list(char *); -void statcmd(void); -void statfilecmd(char *); -void store(char *, char *, int); -void upper(char *); -void user(char *); -void yyerror(char *); -int yyparse(void); -int ls_main(int, char **); - -extern int assumeutf8; -extern char cbuf[]; -extern union sockunion data_dest; -extern int epsvall; -extern int form; -extern int ftpdebug; -extern int guest; -extern union sockunion his_addr; -extern char *homedir; -extern int hostinfo; -extern char *hostname; -extern int maxtimeout; -extern int logged_in; -extern int logging; -extern int noepsv; -extern int noguestretr; -extern int noretr; -extern int paranoid; -extern struct passwd *pw; -extern int pdata; -extern char proctitle[]; -extern int readonly; -extern off_t restart_point; -extern int timeout; -extern char tmpline[]; -extern int type; -extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ -extern int usedefault; - -struct sockaddr_in; -struct sockaddr_in6; -union sockunion { - struct sockinet { - u_char si_len; - u_char si_family; - u_short si_port; - } su_si; - struct sockaddr_in su_sin; - struct sockaddr_in6 su_sin6; -}; -#define su_len su_si.si_len -#define su_family su_si.si_family -#define su_port su_si.si_port diff --git a/libexec/ftpd/ftpchroot.5 b/libexec/ftpd/ftpchroot.5 deleted file mode 100644 index cb2f15f719ad..000000000000 --- a/libexec/ftpd/ftpchroot.5 +++ /dev/null @@ -1,118 +0,0 @@ -.\" Copyright (c) 2003 FreeBSD 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. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 January 26, 2003 -.Dt FTPCHROOT 5 -.Os -.Sh NAME -.Nm ftpchroot -.Nd "list users and groups subject to FTP access restrictions" -.Sh DESCRIPTION -The file -.Nm -is read by -.Xr ftpd 8 -at the beginning of an FTP session, after having authenticated the user. -Each line in -.Nm -corresponds to a user or group. -If a line in -.Nm -matches the current user or a group he is a member of, -access restrictions will be applied to this -session by changing its root directory with -.Xr chroot 2 -to that specified on the line or to the user's login directory. -.Pp -The order of records in -.Nm -is important because the first match will be used. -Fields on each line are separated by tabs or spaces. -.Pp -The first field specifies a user or group name. -If it is prefixed by an -.Dq at -sign, -.Ql @ , -it specifies a group name; -the line will match each user who is a member of this group. -As a special case, a single -.Ql @ -in this field will match any user. -A username is specified otherwise. -.Pp -The optional second field describes the directory for the user -or each member of the group to be locked up in using -.Xr chroot 2 . -Be it omitted, the user's login directory will be used. -If it is not an absolute pathname, then it will be relative -to the user's login directory. -If it contains the -.Pa /./ -separator, -.Xr ftpd 8 -will treat its left-hand side as the name of the directory to do -.Xr chroot 2 -to, and its right-hand side to change the current directory to afterwards. -.Sh FILES -.Bl -tag -width ".Pa /etc/ftpchroot" -compact -.It Pa /etc/ftpchroot -.El -.Sh EXAMPLES -These lines in -.Nm -will lock up the user -.Dq Li webuser -and each member of the group -.Dq Li hostee -in their respective login directories: -.Bd -literal -offset indent -webuser -@hostee -.Ed -.Pp -And this line will tell -.Xr ftpd 8 -to lock up the user -.Dq Li joe -in -.Pa /var/spool/ftp -and then to change the current directory to -.Pa /joe , -which is relative to the session's new root: -.Pp -.Dl "joe /var/spool/ftp/./joe" -.Pp -And finally the following line will lock up every user connecting -through FTP in his respective -.Pa ~/public_html , -thus lowering possible impact on the system -from intrinsic insecurity of FTP: -.Pp -.Dl "@ public_html" -.Sh SEE ALSO -.Xr chroot 2 , -.Xr group 5 , -.Xr passwd 5 , -.Xr ftpd 8 diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y deleted file mode 100644 index c090130d8137..000000000000 --- a/libexec/ftpd/ftpcmd.y +++ /dev/null @@ -1,1806 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -/* - * Grammar for FTP commands. - * See RFC 959. - */ - -%{ - -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> -#include <arpa/ftp.h> - -#include <ctype.h> -#include <errno.h> -#include <glob.h> -#include <libutil.h> -#include <limits.h> -#include <md5.h> -#include <netdb.h> -#include <pwd.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#include "extern.h" -#include "pathnames.h" - -#define yylex ftpcmd_yylex - -off_t restart_point; - -static int cmd_type; -static int cmd_form; -static int cmd_bytesz; -static int state; -char cbuf[512]; -char *fromname = NULL; - -%} - -%union { - struct { - off_t o; - int i; - } u; - char *s; -} - -%token - A B C E F I - L N P R S T - ALL - - SP CRLF COMMA - - USER PASS ACCT REIN QUIT PORT - PASV TYPE STRU MODE RETR STOR - APPE MLFL MAIL MSND MSOM MSAM - MRSQ MRCP ALLO REST RNFR RNTO - ABOR DELE CWD LIST NLST SITE - STAT HELP NOOP MKD RMD PWD - CDUP STOU SMNT SYST SIZE MDTM - LPRT LPSV EPRT EPSV FEAT - - UMASK IDLE CHMOD MDFIVE - - LEXERR NOTIMPL - -%token <s> STRING -%token <u> NUMBER - -%type <u.i> check_login octal_number byte_size -%type <u.i> check_login_ro check_login_epsv -%type <u.i> struct_code mode_code type_code form_code -%type <s> pathstring pathname password username -%type <s> ALL NOTIMPL - -%start cmd_list - -%% - -cmd_list - : /* empty */ - | cmd_list cmd - { - if (fromname) - free(fromname); - fromname = NULL; - restart_point = 0; - } - | cmd_list rcmd - ; - -cmd - : USER SP username CRLF - { - user($3); - free($3); - } - | PASS SP password CRLF - { - pass($3); - free($3); - } - | PASS CRLF - { - pass(""); - } - | PORT check_login SP host_port CRLF - { - if (epsvall) { - reply(501, "No PORT allowed after EPSV ALL."); - goto port_done; - } - if (!$2) - goto port_done; - if (port_check("PORT") == 1) - goto port_done; -#ifdef INET6 - if ((his_addr.su_family != AF_INET6 || - !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { - /* shoud never happen */ - usedefault = 1; - reply(500, "Invalid address rejected."); - goto port_done; - } - port_check_v6("pcmd"); -#endif - port_done: - ; - } - | LPRT check_login SP host_long_port CRLF - { - if (epsvall) { - reply(501, "No LPRT allowed after EPSV ALL."); - goto lprt_done; - } - if (!$2) - goto lprt_done; - if (port_check("LPRT") == 1) - goto lprt_done; -#ifdef INET6 - if (his_addr.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - goto lprt_done; - } - if (port_check_v6("LPRT") == 1) - goto lprt_done; -#endif - lprt_done: - ; - } - | EPRT check_login SP STRING CRLF - { - char delim; - char *tmp = NULL; - char *p, *q; - char *result[3]; - struct addrinfo hints; - struct addrinfo *res; - int i; - - if (epsvall) { - reply(501, "No EPRT allowed after EPSV ALL."); - goto eprt_done; - } - if (!$2) - goto eprt_done; - - memset(&data_dest, 0, sizeof(data_dest)); - tmp = strdup($4); - if (ftpdebug) - syslog(LOG_DEBUG, "%s", tmp); - if (!tmp) { - fatalerror("not enough core"); - /*NOTREACHED*/ - } - 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) { - parsefail: - reply(500, - "Invalid argument, rejected."); - if (tmp) - free(tmp); - usedefault = 1; - goto eprt_done; - } - *q++ = '\0'; - result[i] = p; - if (ftpdebug) - syslog(LOG_DEBUG, "%d: %s", i, p); - p = q; - } - - /* some more sanity check */ - p = result[0]; - while (*p) { - if (!isdigit(*p)) - goto parsefail; - p++; - } - p = result[2]; - while (*p) { - if (!isdigit(*p)) - goto parsefail; - p++; - } - - /* grab address */ - memset(&hints, 0, sizeof(hints)); - if (atoi(result[0]) == 1) - hints.ai_family = PF_INET; -#ifdef INET6 - else if (atoi(result[0]) == 2) - hints.ai_family = PF_INET6; -#endif - else - hints.ai_family = PF_UNSPEC; /*XXX*/ - hints.ai_socktype = SOCK_STREAM; - i = getaddrinfo(result[1], result[2], &hints, &res); - if (i) - goto parsefail; - memcpy(&data_dest, res->ai_addr, 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_sin6.sin6_scope_id = - his_addr.su_sin6.sin6_scope_id; - } -#endif - free(tmp); - tmp = NULL; - - if (port_check("EPRT") == 1) - goto eprt_done; -#ifdef INET6 - if (his_addr.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - goto eprt_done; - } - if (port_check_v6("EPRT") == 1) - goto eprt_done; -#endif - eprt_done: - free($4); - } - | PASV check_login CRLF - { - if (epsvall) - reply(501, "No PASV allowed after EPSV ALL."); - else if ($2) - passive(); - } - | LPSV check_login CRLF - { - if (epsvall) - reply(501, "No LPSV allowed after EPSV ALL."); - else if ($2) - long_passive("LPSV", PF_UNSPEC); - } - | EPSV check_login_epsv SP NUMBER CRLF - { - if ($2) { - int pf; - switch ($4.i) { - case 1: - pf = PF_INET; - break; -#ifdef INET6 - case 2: - pf = PF_INET6; - break; -#endif - default: - pf = -1; /*junk value*/ - break; - } - long_passive("EPSV", pf); - } - } - | EPSV check_login_epsv SP ALL CRLF - { - if ($2) { - reply(200, "EPSV ALL command successful."); - epsvall++; - } - } - | EPSV check_login_epsv CRLF - { - if ($2) - long_passive("EPSV", PF_UNSPEC); - } - | 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 CHAR_BIT == 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 /* CHAR_BIT == 8 */ - UNIMPLEMENTED for CHAR_BIT != 8 -#endif /* CHAR_BIT == 8 */ - } - } - } - | STRU check_login SP struct_code CRLF - { - if ($2) { - switch ($4) { - - case STRU_F: - reply(200, "STRU F accepted."); - 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 accepted."); - break; - - default: - reply(502, "Unimplemented MODE type."); - } - } - } - | 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."); - } - } - | RETR check_login SP pathname CRLF - { - if (noretr || (guest && noguestretr)) - reply(500, "RETR command disabled."); - else if ($2 && $4 != NULL) - retrieve(NULL, $4); - - if ($4 != NULL) - free($4); - } - | STOR check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - store($4, "w", 0); - if ($4 != NULL) - free($4); - } - | APPE check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - store($4, "a", 0); - if ($4 != NULL) - free($4); - } - | NLST check_login CRLF - { - if ($2) - send_file_list("."); - } - | NLST check_login SP pathstring CRLF - { - if ($2) - send_file_list($4); - free($4); - } - | LIST check_login CRLF - { - if ($2) - retrieve(_PATH_LS " -lA", ""); - } - | LIST check_login SP pathstring CRLF - { - if ($2) - retrieve(_PATH_LS " -lA %s", $4); - free($4); - } - | STAT check_login SP pathname CRLF - { - if ($2 && $4 != NULL) - statfilecmd($4); - if ($4 != NULL) - free($4); - } - | STAT check_login CRLF - { - if ($2) { - statcmd(); - } - } - | DELE check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - delete($4); - if ($4 != NULL) - free($4); - } - | RNTO check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) { - if (fromname) { - renamecmd(fromname, $4); - free(fromname); - fromname = NULL; - } else { - reply(503, "Bad sequence of commands."); - } - } - if ($4 != NULL) - free($4); - } - | ABOR check_login CRLF - { - if ($2) - reply(225, "ABOR command successful."); - } - | 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); - } - | 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."); - } - | MKD check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - makedir($4); - if ($4 != NULL) - free($4); - } - | RMD check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - removedir($4); - if ($4 != NULL) - free($4); - } - | PWD check_login CRLF - { - if ($2) - pwd(); - } - | CDUP check_login CRLF - { - if ($2) - cwd(".."); - } - | SITE SP HELP CRLF - { - help(sitetab, NULL); - } - | SITE SP HELP SP STRING CRLF - { - help(sitetab, $5); - free($5); - } - | SITE SP MDFIVE check_login SP pathname CRLF - { - char p[64], *q; - - if ($4 && $6) { - q = MD5File($6, p); - if (q != NULL) - reply(200, "MD5(%s) = %s", $6, p); - else - perror_reply(550, $6); - } - if ($6) - 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) { - if (($6 == -1) || ($6 > 0777)) { - reply(501, "Bad UMASK value."); - } else { - oldmask = umask($6); - reply(200, - "UMASK set to %03o (was %03o).", - $6, oldmask); - } - } - } - | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF - { - if ($4 && ($8 != NULL)) { - if (($6 == -1 ) || ($6 > 0777)) - reply(501, "Bad mode value."); - else if (chmod($8, $6) < 0) - perror_reply(550, $8); - else - reply(200, "CHMOD command successful."); - } - if ($8 != NULL) - free($8); - } - | SITE SP check_login IDLE CRLF - { - if ($3) - reply(200, - "Current IDLE time limit is %d seconds; max %d.", - timeout, maxtimeout); - } - | SITE SP check_login IDLE SP NUMBER CRLF - { - if ($3) { - if ($6.i < 30 || $6.i > maxtimeout) { - reply(501, - "Maximum IDLE time must be between 30 and %d seconds.", - maxtimeout); - } else { - timeout = $6.i; - (void) alarm(timeout); - reply(200, - "Maximum IDLE time set to %d seconds.", - timeout); - } - } - } - | STOU check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - store($4, "w", 1); - if ($4 != NULL) - free($4); - } - | FEAT CRLF - { - lreply(211, "Extensions supported:"); -#if 0 - /* XXX these two keywords are non-standard */ - printf(" EPRT\r\n"); - if (!noepsv) - printf(" EPSV\r\n"); -#endif - printf(" MDTM\r\n"); - printf(" REST STREAM\r\n"); - printf(" SIZE\r\n"); - if (assumeutf8) { - /* TVFS requires UTF8, see RFC 3659 */ - printf(" TVFS\r\n"); - printf(" UTF8\r\n"); - } - reply(211, "End."); - } - | SYST check_login CRLF - { - if ($2) { - if (hostinfo) -#ifdef BSD - reply(215, "UNIX Type: L%d Version: BSD-%d", - CHAR_BIT, BSD); -#else /* BSD */ - reply(215, "UNIX Type: L%d", CHAR_BIT); -#endif /* BSD */ - else - reply(215, "UNKNOWN Type: L%d", CHAR_BIT); - } - } - - /* - * SIZE is not in RFC959, but Postel has blessed it and - * it will be in the updated RFC. - * - * 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); - } - - /* - * MDTM is not in RFC959, but Postel has blessed it and - * it will be in the updated RFC. - * - * 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", - 1900 + t->tm_year, - t->tm_mon+1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); - } - } - if ($4 != NULL) - free($4); - } - | QUIT CRLF - { - reply(221, "Goodbye."); - dologout(0); - } - | NOTIMPL - { - nack($1); - } - | error - { - yyclearin; /* discard lookahead data */ - yyerrok; /* clear error condition */ - state = CMD; /* reset lexer state */ - } - ; -rcmd - : RNFR check_login_ro SP pathname CRLF - { - restart_point = 0; - if ($2 && $4) { - if (fromname) - free(fromname); - fromname = NULL; - if (renamefrom($4)) - fromname = $4; - else - free($4); - } else if ($4) { - free($4); - } - } - | REST check_login SP NUMBER CRLF - { - if ($2) { - if (fromname) - free(fromname); - fromname = NULL; - restart_point = $4.o; - reply(350, "Restarting at %jd. %s", - (intmax_t)restart_point, - "Send STORE or RETRIEVE to initiate transfer."); - } - } - ; - -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; - - data_dest.su_len = sizeof(struct sockaddr_in); - data_dest.su_family = AF_INET; - p = (char *)&data_dest.su_sin.sin_port; - p[0] = $9.i; p[1] = $11.i; - a = (char *)&data_dest.su_sin.sin_addr; - a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; - } - ; - -host_long_port - : 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 - { - char *a, *p; - - memset(&data_dest, 0, sizeof(data_dest)); - data_dest.su_len = sizeof(struct sockaddr_in6); - data_dest.su_family = AF_INET6; - p = (char *)&data_dest.su_port; - p[0] = $39.i; p[1] = $41.i; - a = (char *)&data_dest.su_sin6.sin6_addr; - a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; - a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; - a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; - a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; - if (his_addr.su_family == AF_INET6) { - /* XXX more sanity checks! */ - data_dest.su_sin6.sin6_scope_id = - his_addr.su_sin6.sin6_scope_id; - } - if ($1.i != 6 || $3.i != 16 || $37.i != 2) - memset(&data_dest, 0, sizeof(data_dest)); - } - | 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_sin.sin_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_sin.sin_addr; - a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; - if ($1.i != 4 || $3.i != 4 || $13.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 = CHAR_BIT; - } - | 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 - { - if (logged_in && $1) { - char *p; - - /* - * Expand ~user manually since glob(3) - * will return the unexpanded pathname - * if the corresponding file/directory - * doesn't exist yet. Using sole glob(3) - * would break natural commands like - * MKD ~user/newdir - * or - * RNTO ~/newfile - */ - if ((p = exptilde($1)) != NULL) { - $$ = expglob(p); - free(p); - } else - $$ = NULL; - 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; - } - ; - - -check_login - : /* empty */ - { - $$ = check_login1(); - } - ; - -check_login_epsv - : /* empty */ - { - if (noepsv) { - reply(500, "EPSV command disabled."); - $$ = 0; - } - else - $$ = check_login1(); - } - ; - -check_login_ro - : /* empty */ - { - if (readonly) { - reply(550, "Permission denied."); - $$ = 0; - } - else - $$ = check_login1(); - } - ; - -%% - -#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 /* optional 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 MAXGLOBARGS 1000 - -#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ - -struct tab { - char *name; - short token; - short state; - short implemented; /* 1 if command is implemented */ - char *help; -}; - -struct tab cmdtab[] = { /* In order defined in RFC 765 */ - { "USER", USER, STR1, 1, "<sp> username" }, - { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, - { "ACCT", ACCT, STR1, 0, "(specify account)" }, - { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, - { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, - { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, - { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, - { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, - { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, - { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, - { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, - { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, - { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, - { "STRU", STRU, ARGS, 1, "(specify file structure)" }, - { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, - { "RETR", RETR, STR1, 1, "<sp> file-name" }, - { "STOR", STOR, STR1, 1, "<sp> file-name" }, - { "APPE", APPE, STR1, 1, "<sp> file-name" }, - { "MLFL", MLFL, OSTR, 0, "(mail file)" }, - { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, - { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, - { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, - { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, - { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, - { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, - { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, - { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, - { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, - { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, - { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, - { "DELE", DELE, STR1, 1, "<sp> file-name" }, - { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, - { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, - { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, - { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, - { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, - { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, - { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, - { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, - { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, - { "NOOP", NOOP, ARGS, 1, "" }, - { "MKD", MKD, STR1, 1, "<sp> path-name" }, - { "XMKD", MKD, STR1, 1, "<sp> path-name" }, - { "RMD", RMD, STR1, 1, "<sp> path-name" }, - { "XRMD", RMD, STR1, 1, "<sp> path-name" }, - { "PWD", PWD, ARGS, 1, "(return current directory)" }, - { "XPWD", PWD, ARGS, 1, "(return current directory)" }, - { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, - { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, - { "STOU", STOU, STR1, 1, "<sp> file-name" }, - { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, - { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, - { NULL, 0, 0, 0, 0 } -}; - -struct tab sitetab[] = { - { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, - { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, - { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, - { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, - { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, - { NULL, 0, 0, 0, 0 } -}; - -static char *copy(char *); -static char *expglob(char *); -static char *exptilde(char *); -static void help(struct tab *, char *); -static struct tab * - lookup(struct tab *, char *); -static int port_check(const char *); -#ifdef INET6 -static int port_check_v6(const char *); -#endif -static void sizecmd(char *); -static void toolong(int); -#ifdef INET6 -static void v4map_data_dest(void); -#endif -static int yylex(void); - -static struct tab * -lookup(struct tab *p, char *cmd) -{ - - for (; p->name != NULL; p++) - if (strcmp(cmd, p->name) == 0) - return (p); - return (0); -} - -#include <arpa/telnet.h> - -/* - * get_line - a hacked up version of fgets to ignore TELNET escape codes. - */ -int -get_line(char *s, int n, FILE *iop) -{ - int c; - register char *cs; - sigset_t sset, osset; - - 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 (ftpdebug) - syslog(LOG_DEBUG, "command: %s", s); - tmpline[0] = '\0'; - return(0); - } - if (c == 0) - tmpline[0] = '\0'; - } - /* SIGURG would interrupt stdio if not blocked during the read loop */ - sigemptyset(&sset); - sigaddset(&sset, SIGURG); - sigprocmask(SIG_BLOCK, &sset, &osset); - while ((c = getc(iop)) != EOF) { - c &= 0377; - if (c == IAC) { - if ((c = getc(iop)) == EOF) - goto got_eof; - c &= 0377; - switch (c) { - case WILL: - case WONT: - if ((c = getc(iop)) == EOF) - goto got_eof; - printf("%c%c%c", IAC, DONT, 0377&c); - (void) fflush(stdout); - continue; - case DO: - case DONT: - if ((c = getc(iop)) == EOF) - goto got_eof; - printf("%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. - */ - while (c != '\n' && (c = getc(iop)) != EOF) - ; - return (-2); - } - if (c == '\n') - break; - } -got_eof: - sigprocmask(SIG_SETMASK, &osset, NULL); - if (c == EOF && cs == s) - return (-1); - *cs++ = '\0'; - if (ftpdebug) { - if (!guest && strncasecmp("pass ", s, 5) == 0) { - /* Don't syslog passwords */ - syslog(LOG_DEBUG, "command: %.5s ???", s); - } else { - register char *cp; - register 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); -} - -static void -toolong(int signo) -{ - - reply(421, - "Timeout (%d seconds): closing control connection.", timeout); - if (logging) - syslog(LOG_INFO, "User %s timed out after %d seconds", - (pw ? pw -> pw_name : "unknown"), timeout); - dologout(1); -} - -static int -yylex(void) -{ - static int cpos; - char *cp, *cp2; - struct tab *p; - int n; - char c; - - for (;;) { - switch (state) { - - case CMD: - (void) signal(SIGALRM, toolong); - (void) alarm(timeout); - n = get_line(cbuf, sizeof(cbuf)-1, stdin); - if (n == -1) { - reply(221, "You could at least say goodbye."); - dologout(0); - } else if (n == -2) { - reply(500, "Command too long."); - (void) alarm(0); - continue; - } - (void) alarm(0); -#ifdef SETPROCTITLE - if (strncasecmp(cbuf, "PASS", 4) != 0) - setproctitle("%s: %s", proctitle, cbuf); -#endif /* SETPROCTITLE */ - if ((cp = strchr(cbuf, '\r'))) { - *cp++ = '\n'; - *cp = '\0'; - } - if ((cp = strpbrk(cbuf, " \n"))) - cpos = cp - cbuf; - if (cpos == 0) - cpos = 4; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - upper(cbuf); - p = lookup(cmdtab, cbuf); - cbuf[cpos] = c; - if (p != 0) { - yylval.s = p->name; - if (!p->implemented) - return (NOTIMPL); /* state remains CMD */ - state = p->state; - return (p->token); - } - break; - - case SITECMD: - if (cbuf[cpos] == ' ') { - cpos++; - return (SP); - } - cp = &cbuf[cpos]; - if ((cp2 = strpbrk(cp, " \n"))) - cpos = cp2 - cbuf; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - upper(cp); - p = lookup(sitetab, cp); - cbuf[cpos] = c; - if (guest == 0 && p != 0) { - yylval.s = p->name; - if (!p->implemented) { - state = CMD; - return (NOTIMPL); - } - state = p->state; - return (p->token); - } - state = CMD; - break; - - case ZSTR1: - case OSTR: - if (cbuf[cpos] == '\n') { - state = CMD; - return (CRLF); - } - /* FALLTHROUGH */ - - case STR1: - dostr1: - if (cbuf[cpos] == ' ') { - cpos++; - state = state == OSTR ? STR2 : state+1; - return (SP); - } - break; - - case ZSTR2: - if (cbuf[cpos] == '\n') { - state = CMD; - return (CRLF); - } - /* FALLTHROUGH */ - - case STR2: - cp = &cbuf[cpos]; - n = strlen(cp); - cpos += n - 1; - /* - * Make sure the string is nonempty and \n terminated. - */ - if (n > 1 && cbuf[cpos] == '\n') { - cbuf[cpos] = '\0'; - yylval.s = copy(cp); - cbuf[cpos] = '\n'; - state = ARGS; - return (STRING); - } - break; - - case NSTR: - if (cbuf[cpos] == ' ') { - cpos++; - return (SP); - } - if (isdigit(cbuf[cpos])) { - cp = &cbuf[cpos]; - while (isdigit(cbuf[++cpos])) - ; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - yylval.u.i = atoi(cp); - cbuf[cpos] = c; - state = STR1; - return (NUMBER); - } - state = STR1; - goto dostr1; - - case ARGS: - if (isdigit(cbuf[cpos])) { - cp = &cbuf[cpos]; - while (isdigit(cbuf[++cpos])) - ; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - yylval.u.i = atoi(cp); - yylval.u.o = strtoull(cp, NULL, 10); - cbuf[cpos] = c; - return (NUMBER); - } - if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 - && !isalnum(cbuf[cpos + 3])) { - cpos += 3; - return ALL; - } - switch (cbuf[cpos++]) { - - case '\n': - state = CMD; - 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; - - default: - fatalerror("Unknown state in scanner."); - } - state = CMD; - return (LEXERR); - } -} - -void -upper(char *s) -{ - while (*s != '\0') { - if (islower(*s)) - *s = toupper(*s); - s++; - } -} - -static char * -copy(char *s) -{ - char *p; - - p = malloc(strlen(s) + 1); - if (p == NULL) - fatalerror("Ran out of memory."); - (void) strcpy(p, s); - return (p); -} - -static void -help(struct tab *ctab, char *s) -{ - struct tab *c; - int width, NCMDS; - char *type; - - if (ctab == sitetab) - type = "SITE "; - else - type = ""; - 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; - - lreply(214, "The following %scommands are recognized %s.", - type, "(* =>'s unimplemented)"); - columns = 76 / width; - if (columns == 0) - columns = 1; - lines = (NCMDS + columns - 1) / columns; - for (i = 0; i < lines; i++) { - printf(" "); - for (j = 0; j < columns; j++) { - c = ctab + j * lines + i; - printf("%s%c", c->name, - c->implemented ? ' ' : '*'); - if (c + lines >= &ctab[NCMDS]) - break; - w = strlen(c->name) + 1; - while (w < width) { - putchar(' '); - w++; - } - } - printf("\r\n"); - } - (void) fflush(stdout); - if (hostinfo) - reply(214, "Direct comments to ftp-bugs@%s.", hostname); - else - reply(214, "End."); - return; - } - upper(s); - c = lookup(ctab, s); - if (c == NULL) { - reply(502, "Unknown command %s.", s); - return; - } - if (c->implemented) - reply(214, "Syntax: %s%s %s", type, c->name, c->help); - else - reply(214, "%s%-*s\t%s; unimplemented.", type, width, - c->name, c->help); -} - -static void -sizecmd(char *filename) -{ - switch (type) { - case TYPE_L: - case TYPE_I: { - struct stat stbuf; - if (stat(filename, &stbuf) < 0) - perror_reply(550, filename); - else if (!S_ISREG(stbuf.st_mode)) - reply(550, "%s: not a plain file.", filename); - else - reply(213, "%jd", (intmax_t)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) { - perror_reply(550, filename); - (void) fclose(fin); - return; - } else if (!S_ISREG(stbuf.st_mode)) { - reply(550, "%s: not a plain file.", filename); - (void) fclose(fin); - return; - } else if (stbuf.st_size > MAXASIZE) { - reply(550, "%s: too large for type A 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, "%jd", (intmax_t)count); - break; } - default: - reply(504, "SIZE not implemented for type %s.", - typenames[type]); - } -} - -/* Return 1, if port check is done. Return 0, if not yet. */ -static int -port_check(const char *pcmd) -{ - if (his_addr.su_family == AF_INET) { - if (data_dest.su_family != AF_INET) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return 1; - } - if (paranoid && - ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || - memcmp(&data_dest.su_sin.sin_addr, - &his_addr.su_sin.sin_addr, - sizeof(data_dest.su_sin.sin_addr)))) { - usedefault = 1; - reply(500, "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "%s command successful.", pcmd); - } - return 1; - } - return 0; -} - -static int -check_login1(void) -{ - if (logged_in) - return 1; - else { - reply(530, "Please login with USER and PASS."); - return 0; - } -} - -/* - * Replace leading "~user" in a pathname by the user's login directory. - * Returned string will be in a freshly malloced buffer unless it's NULL. - */ -static char * -exptilde(char *s) -{ - char *p, *q; - char *path, *user; - struct passwd *ppw; - - if ((p = strdup(s)) == NULL) - return (NULL); - if (*p != '~') - return (p); - - user = p + 1; /* skip tilde */ - if ((path = strchr(p, '/')) != NULL) - *(path++) = '\0'; /* separate ~user from the rest of path */ - if (*user == '\0') /* no user specified, use the current user */ - user = pw->pw_name; - /* read passwd even for the current user since we may be chrooted */ - if ((ppw = getpwnam(user)) != NULL) { - /* user found, substitute login directory for ~user */ - if (path) - asprintf(&q, "%s/%s", ppw->pw_dir, path); - else - q = strdup(ppw->pw_dir); - free(p); - p = q; - } else { - /* user not found, undo the damage */ - if (path) - path[-1] = '/'; - } - return (p); -} - -/* - * Expand glob(3) patterns possibly present in a pathname. - * Avoid expanding to a pathname including '\r' or '\n' in order to - * not disrupt the FTP protocol. - * The expansion found must be unique. - * Return the result as a malloced string, or NULL if an error occurred. - * - * 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. - */ -static char * -expglob(char *s) -{ - char *p, **pp, *rval; - int flags = GLOB_BRACE | GLOB_NOCHECK; - int n; - glob_t gl; - - memset(&gl, 0, sizeof(gl)); - flags |= GLOB_LIMIT; - gl.gl_matchc = MAXGLOBARGS; - if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { - for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) - if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { - p = *pp; - n++; - } - if (n == 0) - rval = strdup(s); - else if (n == 1) - rval = strdup(p); - else { - reply(550, "Wildcard is ambiguous."); - rval = NULL; - } - } else { - reply(550, "Wildcard expansion error."); - rval = NULL; - } - globfree(&gl); - return (rval); -} - -#ifdef INET6 -/* Return 1, if port check is done. Return 0, if not yet. */ -static int -port_check_v6(const char *pcmd) -{ - if (his_addr.su_family == AF_INET6) { - if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) - /* Convert data_dest into v4 mapped sockaddr.*/ - v4map_data_dest(); - if (data_dest.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return 1; - } - if (paranoid && - ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || - memcmp(&data_dest.su_sin6.sin6_addr, - &his_addr.su_sin6.sin6_addr, - sizeof(data_dest.su_sin6.sin6_addr)))) { - usedefault = 1; - reply(500, "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "%s command successful.", pcmd); - } - return 1; - } - return 0; -} - -static void -v4map_data_dest(void) -{ - struct in_addr savedaddr; - int savedport; - - if (data_dest.su_family != AF_INET) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return; - } - - savedaddr = data_dest.su_sin.sin_addr; - savedport = data_dest.su_port; - - memset(&data_dest, 0, sizeof(data_dest)); - data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); - data_dest.su_sin6.sin6_family = AF_INET6; - data_dest.su_sin6.sin6_port = savedport; - memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); - memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], - (caddr_t)&savedaddr, sizeof(savedaddr)); -} -#endif diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 deleted file mode 100644 index 96db4753209e..000000000000 --- a/libexec/ftpd/ftpd.8 +++ /dev/null @@ -1,589 +0,0 @@ -.\" Copyright (c) 1985, 1988, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd June 26, 2025 -.Dt FTPD 8 -.Os -.Sh NAME -.Nm ftpd -.Nd Internet File Transfer Protocol server -.Sh SYNOPSIS -.Nm -.Op Fl 468BDdEhMmOoRrSUvW -.Bq Fl A | Fl n -.Op Fl l Op Fl l -.Op Fl a Ar address -.Op Fl P Ar port -.Op Fl p Ar file -.Op Fl T Ar maxtimeout -.Op Fl t Ar timeout -.Op Fl u Ar umask -.Sh DEPRECATION NOTICE -The -.Fx -base system -.Nm -is deprecated, and will be removed in -.Fx 15.0 . -Users are advised to install the -.Pa ftp/freebsd-ftpd -port or package instead. -.Sh DESCRIPTION -The -.Nm -utility is the -Internet File Transfer Protocol -server process. -The server uses the TCP protocol -and listens at the port specified with the -.Fl P -option or in the -.Dq ftp -service specification; see -.Xr services 5 . -.Pp -Available options: -.Bl -tag -width indent -.It Fl 4 -When -.Fl D -is specified, accept connections via -.Dv AF_INET -socket. -.It Fl 6 -When -.Fl D -is specified, accept connections via -.Dv AF_INET6 -socket. -.It Fl 8 -Enable transparent UTF-8 mode. -RFC\ 2640 compliant clients will be told that the character encoding -used by the server is UTF-8, which is the only effect of the option. -.Pp -This option does not enable any encoding conversion for server file names; -it implies instead that the names of files on the server are encoded -in UTF-8. -As for files uploaded via FTP, it is the duty of the RFC\ 2640 compliant -client to convert their names from the client's local encoding to UTF-8. -FTP command names and own -.Nm -messages are always encoded in ASCII, which is a subset of UTF-8. -Hence no need for server-side conversion at all. -.It Fl A -Allow only anonymous ftp access. -.It Fl a -When -.Fl D -is specified, accept connections only on the specified -.Ar address . -.It Fl B -With this option set, -.Nm -sends authentication success and failure messages to the -.Xr blacklistd 8 -daemon. -If this option is not specified, no communcation with the -.Xr blacklistd 8 -daemon is attempted. -.It Fl D -With this option set, -.Nm -will detach and become a daemon, accepting connections on the FTP port and -forking children processes to handle them. -This is lower overhead than starting -.Nm -from -.Xr inetd 8 -and is thus useful on busy servers to reduce load. -.It Fl d -Debugging information is written to the syslog using -.Dv LOG_FTP . -.It Fl E -Disable the EPSV command. -This is useful for servers behind older firewalls. -.It Fl h -Disable printing host-specific information, such as the -server software version or hostname, in server messages. -.It Fl l -Each successful and failed -.Xr ftp 1 -session is logged using syslog with a facility of -.Dv LOG_FTP . -If this option is specified twice, the retrieve (get), store (put), append, -delete, make directory, remove directory and rename operations and -their filename arguments are also logged. -By default, -.Xr syslogd 8 -logs these to -.Pa /var/log/xferlog . -.It Fl M -Prevent anonymous users from creating directories. -.It Fl m -Permit anonymous users to overwrite or modify -existing files if allowed by file system permissions. -By default, anonymous users cannot modify existing files; -in particular, files to upload will be created under a unique name. -.It Fl n -Disable anonymous FTP access. -The -.Fl n -option is mutually exclusive with the -.Fl A -option. -.It Fl O -Put server in write-only mode for anonymous users only. -RETR is disabled for anonymous users, preventing anonymous downloads. -This has no effect if -.Fl o -is also specified. -.It Fl o -Put server in write-only mode. -RETR is disabled, preventing downloads. -.It Fl P -When -.Fl D -is specified, accept connections at -.Ar port , -specified as a numeric value or service name, instead of at the default -.Dq ftp -port. -.It Fl p -When -.Fl D -is specified, write the daemon's process ID to -.Ar file -instead of the default pid file, -.Pa /var/run/ftpd.pid . -.It Fl R -With this option set, -.Nm -will revert to historical behavior with regard to security checks on -user operations and restrictions on PORT requests. -Currently, -.Nm -will only honor PORT commands directed to unprivileged ports on the -remote user's host (which violates the FTP protocol specification but -closes some security holes). -.It Fl r -Put server in read-only mode. -All commands which may modify the local file system are disabled. -.It Fl S -With this option set, -.Nm -logs all anonymous file downloads to the file -.Pa /var/log/ftpd -when this file exists. -.It Fl T -A client may also request a different timeout period; -the maximum period allowed may be set to -.Ar timeout -seconds with the -.Fl T -option. -The default limit is 2 hours. -.It Fl t -The inactivity timeout period is set to -.Ar timeout -seconds (the default is 15 minutes). -.It Fl U -This option instructs ftpd to use data ports in the range of -.Dv IP_PORTRANGE_DEFAULT -instead of in the range of -.Dv IP_PORTRANGE_HIGH . -Such a change may be useful for some specific firewall configurations; -see -.Xr ip 4 -for more information. -.Pp -Note that option is a virtual no-op in -.Fx 5.0 -and above; both port -ranges are identical by default. -.It Fl u -The default file creation mode mask is set to -.Ar umask , -which is expected to be an octal numeric value. -Refer to -.Xr umask 2 -for details. -This option may be overridden by -.Xr login.conf 5 . -.It Fl v -A synonym for -.Fl d . -.It Fl W -Do not log FTP sessions to the user accounting database. -.El -.Pp -The file -.Pa /var/run/nologin -can be used to disable ftp access. -If the file exists, -.Nm -displays it and exits. -If the file -.Pa /etc/ftpwelcome -exists, -.Nm -prints it before issuing the -.Dq ready -message. -If the file -.Pa /etc/ftpmotd -exists, -.Nm -prints it after a successful login. -Note the motd file used is the one -relative to the login environment. -This means the one in -.Pa ~ftp/etc -in the anonymous user's case. -.Pp -The ftp server currently supports the following ftp requests. -The case of the requests is ignored. -Requests marked [RW] are -disabled if -.Fl r -is specified. -.Bl -column "Request" -offset indent -.It Sy Request Ta Sy "Description" -.It ABOR Ta "abort previous command" -.It ACCT Ta "specify account (ignored)" -.It ALLO Ta "allocate storage (vacuously)" -.It APPE Ta "append to a file [RW]" -.It CDUP Ta "change to parent of current working directory" -.It CWD Ta "change working directory" -.It DELE Ta "delete a file [RW]" -.It EPRT Ta "specify data connection port, multiprotocol" -.It EPSV Ta "prepare for server-to-server transfer, multiprotocol" -.It FEAT Ta "give information on extended features of server" -.It HELP Ta "give help information" -.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lA" -.It LPRT Ta "specify data connection port, multiprotocol" -.It LPSV Ta "prepare for server-to-server transfer, multiprotocol" -.It MDTM Ta "show last modification time of file" -.It MKD Ta "make a directory [RW]" -.It MODE Ta "specify data transfer" Em mode -.It NLST Ta "give name list of files in directory" -.It NOOP Ta "do nothing" -.It PASS Ta "specify password" -.It PASV Ta "prepare for server-to-server transfer" -.It PORT Ta "specify data connection port" -.It PWD Ta "print the current working directory" -.It QUIT Ta "terminate session" -.It REST Ta "restart incomplete transfer" -.It RETR Ta "retrieve a file" -.It RMD Ta "remove a directory [RW]" -.It RNFR Ta "specify rename-from file name [RW]" -.It RNTO Ta "specify rename-to file name [RW]" -.It SITE Ta "non-standard commands (see next section)" -.It SIZE Ta "return size of file" -.It STAT Ta "return status of server" -.It STOR Ta "store a file [RW]" -.It STOU Ta "store a file with a unique name [RW]" -.It STRU Ta "specify data transfer" Em structure -.It SYST Ta "show operating system type of server system" -.It TYPE Ta "specify data transfer" Em type -.It USER Ta "specify user name" -.It XCUP Ta "change to parent of current working directory (deprecated)" -.It XCWD Ta "change working directory (deprecated)" -.It XMKD Ta "make a directory (deprecated) [RW]" -.It XPWD Ta "print the current working directory (deprecated)" -.It XRMD Ta "remove a directory (deprecated) [RW]" -.El -.Pp -The following non-standard or -.Ux -specific commands are supported -by the -SITE request. -.Bl -column Request -offset indent -.It Sy Request Ta Sy Description -.It UMASK Ta change umask, e.g. ``SITE UMASK 002'' -.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' -.It CHMOD Ta "change mode of a file [RW], e.g. ``SITE CHMOD 755 filename''" -.It MD5 Ta "report the files MD5 checksum, e.g. ``SITE MD5 filename''" -.It HELP Ta give help information -.El -.Pp -Note: SITE requests are disabled in case of anonymous logins. -.Pp -The remaining ftp requests specified in Internet RFC 959 -are -recognized, but not implemented. -MDTM and SIZE are not specified in RFC 959, but will appear in the -next updated FTP RFC. -To avoid possible denial-of-service attacks, SIZE requests against -files larger than 10240 bytes will be denied if the current transfer -type is ASCII. -.Pp -The ftp server will abort an active file transfer only when the -ABOR -command is preceded by a Telnet "Interrupt Process" (IP) -signal and a Telnet "Synch" signal in the command Telnet stream, -as described in Internet RFC 959. -If a -STAT -command is received during a data transfer, preceded by a Telnet IP -and Synch, transfer status will be returned. -.Pp -The -.Nm -utility interprets file names according to the -.Dq globbing -conventions used by -.Xr csh 1 . -This allows users to utilize the metacharacters -.Dq Li \&*?[]{}~ . -.Pp -The -.Nm -utility authenticates users according to six rules. -.Bl -enum -offset indent -.It -The login name must be in the password data base -and not have a null password. -In this case a password must be provided by the client before any -file operations may be performed. -.It -The login name must not appear in the file -.Pa /etc/ftpusers . -.It -The login name must not be a member of a group specified in the file -.Pa /etc/ftpusers . -Entries in this file interpreted as group names are prefixed by an "at" -.Ql \&@ -sign. -.It -The user must have a standard shell returned by -.Xr getusershell 3 . -.It -If the user name appears in the file -.Pa /etc/ftpchroot , -or the user is a member of a group with a group entry in this file, -i.e., one prefixed with -.Ql \&@ , -the session's root will be changed to the directory specified -in this file or to the user's login directory by -.Xr chroot 2 -as for an -.Dq anonymous -or -.Dq ftp -account (see next item). -See -.Xr ftpchroot 5 -for a detailed description of the format of this file. -This facility may also be triggered by enabling the boolean "ftp-chroot" -capability in -.Xr login.conf 5 . -However, the user must still supply a password. -This feature is intended as a compromise between a fully anonymous -account and a fully privileged account. -The account should also be set up as for an anonymous account. -.It -If the user name is -.Dq anonymous -or -.Dq ftp , -an -anonymous ftp account must be present in the password -file (user -.Dq ftp ) . -In this case the user is allowed -to log in by specifying any password (by convention an email address for -the user should be used as the password). -When the -.Fl S -option is set, all transfers are logged as well. -.El -.Pp -In the last case, -.Nm -takes special measures to restrict the client's access privileges. -The server performs a -.Xr chroot 2 -to the home directory of the -.Dq ftp -user. -As a special case if the -.Dq ftp -user's home directory pathname contains the -.Pa /./ -separator, -.Nm -uses its left-hand side as the name of the directory to do -.Xr chroot 2 -to, and its right-hand side to change the current directory to afterwards. -A typical example for this case would be -.Pa /var/spool/ftp/./pub . -In order that system security is not breached, it is recommended -that the -.Dq ftp -subtree be constructed with care, following these rules: -.Bl -tag -width "~ftp/pub" -offset indent -.It Pa ~ftp -Make the home directory owned by -.Dq root -and unwritable by anyone. -.It Pa ~ftp/etc -Make this directory owned by -.Dq root -and unwritable by anyone (mode 555). -The files pwd.db (see -.Xr passwd 5 ) -and -.Xr group 5 -must be present for the -.Xr ls 1 -command to be able to produce owner names rather than numbers. -The password field in -.Xr passwd 5 -is not used, and should not contain real passwords. -The file -.Pa ftpmotd , -if present, will be printed after a successful login. -These files should be mode 444. -.It Pa ~ftp/pub -This directory and the subdirectories beneath it should be owned -by the users and groups responsible for placing files in them, -and be writable only by them (mode 755 or 775). -They should -.Em not -be owned or writable by -.Dq ftp -or its group, otherwise guest users -can fill the drive with unwanted files. -.El -.Pp -If the system has multiple IP addresses, -.Nm -supports the idea of virtual hosts, which provides the ability to -define multiple anonymous ftp areas, each one allocated to a different -internet address. -The file -.Pa /etc/ftphosts -contains information pertaining to each of the virtual hosts. -Each host is defined on its own line which contains a number of -fields separated by whitespace: -.Bl -tag -offset indent -width hostname -.It hostname -Contains the hostname or IP address of the virtual host. -.It user -Contains a user record in the system password file. -As with normal anonymous ftp, this user's access uid, gid and group -memberships determine file access to the anonymous ftp area. -The anonymous ftp area (to which any user is chrooted on login) -is determined by the home directory defined for the account. -User id and group for any ftp account may be the same as for the -standard ftp user. -.It statfile -File to which all file transfers are logged, which -defaults to -.Pa /var/log/ftpd . -.It welcome -This file is the welcome message displayed before the server ready -prompt. -It defaults to -.Pa /etc/ftpwelcome . -.It motd -This file is displayed after the user logs in. -It defaults to -.Pa /etc/ftpmotd . -.El -.Pp -Lines beginning with a '#' are ignored and can be used to include -comments. -.Pp -Defining a virtual host for the primary IP address or hostname -changes the default for ftp logins to that address. -The 'user', 'statfile', 'welcome' and 'motd' fields may be left -blank, or a single hyphen '-' used to indicate that the default -value is to be used. -.Pp -As with any anonymous login configuration, due care must be given -to setup and maintenance to guard against security related problems. -.Pp -The -.Nm -utility has internal support for handling remote requests to list -files, and will not execute -.Pa /bin/ls -in either a chrooted or non-chrooted environment. -The -.Pa ~/bin/ls -executable need not be placed into the chrooted tree, nor need the -.Pa ~/bin -directory exist. -.Sh FILES -.Bl -tag -width ".Pa /var/run/ftpd.pid" -compact -.It Pa /etc/ftpusers -List of unwelcome/restricted users. -.It Pa /etc/ftpchroot -List of normal users who should be chroot'd. -.It Pa /etc/ftphosts -Virtual hosting configuration file. -.It Pa /etc/ftpwelcome -Welcome notice. -.It Pa /etc/ftpmotd -Welcome notice after login. -.It Pa /var/run/ftpd.pid -Default pid file for daemon mode. -.It Pa /var/run/nologin -Displayed and access refused. -.It Pa /var/log/ftpd -Log file for anonymous transfers. -.It Pa /var/log/xferlog -Default place for session logs. -.It Pa /var/spool/ftp -Recommended directory for the FTP root directory -(the home directory of the ftp user). -.El -.Sh SEE ALSO -.Xr ftp 1 , -.Xr umask 2 , -.Xr getusershell 3 , -.Xr ftpchroot 5 , -.Xr login.conf 5 , -.Xr inetd 8 , -.Xr syslogd 8 -.Sh HISTORY -The -.Nm -utility appeared in -.Bx 4.2 . -IPv6 support was added in WIDE Hydrangea IPv6 stack kit. -.Sh BUGS -The server must run as the super-user -to create sockets with privileged port numbers. -It maintains -an effective user id of the logged in user, reverting to -the super-user only when binding addresses to sockets. -The -possible security holes have been extensively -scrutinized, but are possibly incomplete. diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c deleted file mode 100644 index 751d77b218b7..000000000000 --- a/libexec/ftpd/ftpd.c +++ /dev/null @@ -1,3446 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -/* - * FTP server. - */ -#include <sys/param.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> - -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/tcp.h> - -#define FTP_NAMES -#include <arpa/ftp.h> -#include <arpa/inet.h> -#include <arpa/telnet.h> - -#include <ctype.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <glob.h> -#include <limits.h> -#include <netdb.h> -#include <pwd.h> -#include <grp.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> -#include <libutil.h> -#ifdef LOGIN_CAP -#include <login_cap.h> -#endif - -#ifdef USE_PAM -#include <security/pam_appl.h> -#endif - -#include "blacklist_client.h" -#include "pathnames.h" -#include "extern.h" - -#include <stdarg.h> - -static char version[] = "Version 6.00LS"; -#undef main - -union sockunion ctrl_addr; -union sockunion data_source; -union sockunion data_dest; -union sockunion his_addr; -union sockunion pasv_addr; - -int daemon_mode; -int data; -int dataport; -int hostinfo = 1; /* print host-specific info in messages */ -int logged_in; -struct passwd *pw; -char *homedir; -int ftpdebug; -int timeout = 900; /* timeout after 15 minutes of inactivity */ -int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ -int logging; -int restricted_data_ports = 1; -int paranoid = 1; /* be extra careful about security */ -int anon_only = 0; /* Only anonymous ftp allowed */ -int noanon = 0; /* disable anonymous ftp */ -int assumeutf8 = 0; /* Assume that server file names are in UTF-8 */ -int guest; -int dochroot; -char *chrootdir; -int dowtmp = 1; -int stats; -int statfd = -1; -int type; -int form; -int stru; /* avoid C keyword */ -int mode; -int usedefault = 1; /* for data transfers */ -int pdata = -1; /* for passive mode */ -int readonly = 0; /* Server is in readonly mode. */ -int noepsv = 0; /* EPSV command is disabled. */ -int noretr = 0; /* RETR command is disabled. */ -int noguestretr = 0; /* RETR command is disabled for anon users. */ -int noguestmkd = 0; /* MKD command is disabled for anon users. */ -int noguestmod = 1; /* anon users may not modify existing files. */ -int use_blacklist = 0; - -off_t file_size; -off_t byte_count; -#if !defined(CMASK) || CMASK == 0 -#undef CMASK -#define CMASK 027 -#endif -int defumask = CMASK; /* default umask value */ -char tmpline[7]; -char *hostname; -int epsvall = 0; - -#ifdef VIRTUAL_HOSTING -char *ftpuser; - -static struct ftphost { - struct ftphost *next; - struct addrinfo *hostinfo; - char *hostname; - char *anonuser; - char *statfile; - char *welcome; - char *loginmsg; -} *thishost, *firsthost; - -#endif -char remotehost[NI_MAXHOST]; -char *ident = NULL; - -static char wtmpid[20]; - -#ifdef USE_PAM -static int auth_pam(struct passwd**, const char*); -pam_handle_t *pamh = NULL; -#endif - -char *pid_file = NULL; /* means default location to pidfile(3) */ - -/* - * Limit number of pathnames that glob can return. - * A limit of 0 indicates the number of pathnames is unlimited. - */ -#define MAXGLOBARGS 16384 -# - -/* - * 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; - -#ifdef SETPROCTITLE -char proctitle[LINE_MAX]; /* initial part of title */ -#endif /* SETPROCTITLE */ - -#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) -#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) -#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) - -static volatile sig_atomic_t recvurg; -static int transflag; /* NB: for debugging only */ - -#define STARTXFER flagxfer(1) -#define ENDXFER flagxfer(0) - -#define START_UNSAFE maskurg(1) -#define END_UNSAFE maskurg(0) - -/* It's OK to put an `else' clause after this macro. */ -#define CHECKOOB(action) \ - if (recvurg) { \ - recvurg = 0; \ - if (myoob()) { \ - ENDXFER; \ - action; \ - } \ - } - -#ifdef VIRTUAL_HOSTING -static void inithosts(int); -static void selecthost(union sockunion *); -#endif -static void ack(char *); -static void sigurg(int); -static void maskurg(int); -static void flagxfer(int); -static int myoob(void); -static int checkuser(char *, char *, int, char **, int *); -static FILE *dataconn(char *, off_t, char *); -static void dolog(struct sockaddr *); -static void end_login(void); -static FILE *getdatasock(char *); -static int guniquefd(char *, char **); -static void lostconn(int); -static void sigquit(int); -static int receive_data(FILE *, FILE *); -static int send_data(FILE *, FILE *, size_t, off_t, int); -static struct passwd * - sgetpwnam(char *); -static char *sgetsave(char *); -static void reapchild(int); -static void appendf(char **, char *, ...) __printflike(2, 3); -static void logcmd(char *, char *, char *, off_t); -static void logxfer(char *, off_t, time_t); -static char *doublequote(char *); -static int *socksetup(int, char *, const char *); - -int -main(int argc, char *argv[], char **envp) -{ - socklen_t addrlen; - int ch, on = 1, tos, s = STDIN_FILENO; - char *cp, line[LINE_MAX]; - FILE *fd; - char *bindname = NULL; - const char *bindport = "ftp"; - int family = AF_UNSPEC; - struct sigaction sa; - - tzset(); /* in case no timezone database in ~ftp */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - /* - * Prevent diagnostic messages from appearing on stderr. - * We run as a daemon or from inetd; in both cases, there's - * more reason in logging to syslog. - */ - (void) freopen(_PATH_DEVNULL, "w", stderr); - opterr = 0; - - /* - * 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, - "468a:ABdDEhlmMnoOp:P:rRSt:T:u:UvW")) != -1) { - switch (ch) { - case '4': - family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; - break; - - case '6': - family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; - break; - - case '8': - assumeutf8 = 1; - break; - - case 'a': - bindname = optarg; - break; - - case 'A': - anon_only = 1; - break; - - case 'B': -#ifdef USE_BLACKLIST - use_blacklist = 1; -#else - syslog(LOG_WARNING, "not compiled with USE_BLACKLIST support"); -#endif - break; - - case 'd': - ftpdebug++; - break; - - case 'D': - daemon_mode++; - break; - - case 'E': - noepsv = 1; - break; - - case 'h': - hostinfo = 0; - break; - - case 'l': - logging++; /* > 1 == extra logging */ - break; - - case 'm': - noguestmod = 0; - break; - - case 'M': - noguestmkd = 1; - break; - - case 'n': - noanon = 1; - break; - - case 'o': - noretr = 1; - break; - - case 'O': - noguestretr = 1; - break; - - case 'p': - pid_file = optarg; - break; - - case 'P': - bindport = optarg; - break; - - case 'r': - readonly = 1; - break; - - case 'R': - paranoid = 0; - break; - - case 'S': - stats++; - break; - - case 't': - timeout = atoi(optarg); - if (maxtimeout < timeout) - maxtimeout = timeout; - break; - - case 'T': - maxtimeout = atoi(optarg); - if (timeout > maxtimeout) - timeout = maxtimeout; - break; - - case 'u': - { - long val = 0; - - val = strtol(optarg, &optarg, 8); - if (*optarg != '\0' || val < 0) - syslog(LOG_WARNING, "bad value for -u"); - else - defumask = val; - break; - } - case 'U': - restricted_data_ports = 0; - break; - - case 'v': - ftpdebug++; - break; - - case 'W': - dowtmp = 0; - break; - - default: - syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); - break; - } - } - - if (noanon && anon_only) { - syslog(LOG_ERR, "-n and -A are mutually exclusive"); - exit(1); - } - - /* handle filesize limit gracefully */ - sa.sa_handler = SIG_IGN; - (void)sigaction(SIGXFSZ, &sa, NULL); - - if (daemon_mode) { - int *ctl_sock, fd, maxfd = -1, nfds, i; - fd_set defreadfds, readfds; - pid_t pid; - struct pidfh *pfh; - - if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { - if (errno == EEXIST) { - syslog(LOG_ERR, "%s already running, pid %d", - getprogname(), (int)pid); - exit(1); - } - syslog(LOG_WARNING, "pidfile_open: %m"); - } - - /* - * Detach from parent. - */ - if (daemon(1, 1) < 0) { - syslog(LOG_ERR, "failed to become a daemon"); - exit(1); - } - - if (pfh != NULL && pidfile_write(pfh) == -1) - syslog(LOG_WARNING, "pidfile_write: %m"); - - sa.sa_handler = reapchild; - (void)sigaction(SIGCHLD, &sa, NULL); - -#ifdef VIRTUAL_HOSTING - inithosts(family); -#endif - - /* - * Open a socket, bind it to the FTP port, and start - * listening. - */ - ctl_sock = socksetup(family, bindname, bindport); - if (ctl_sock == NULL) - exit(1); - - FD_ZERO(&defreadfds); - for (i = 1; i <= *ctl_sock; i++) { - FD_SET(ctl_sock[i], &defreadfds); - if (listen(ctl_sock[i], 32) < 0) { - syslog(LOG_ERR, "control listen: %m"); - exit(1); - } - if (maxfd < ctl_sock[i]) - maxfd = ctl_sock[i]; - } - - /* - * Loop forever accepting connection requests and forking off - * children to handle them. - */ - while (1) { - FD_COPY(&defreadfds, &readfds); - nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); - if (nfds <= 0) { - if (nfds < 0 && errno != EINTR) - syslog(LOG_WARNING, "select: %m"); - continue; - } - - pid = -1; - for (i = 1; i <= *ctl_sock; i++) - if (FD_ISSET(ctl_sock[i], &readfds)) { - addrlen = sizeof(his_addr); - fd = accept(ctl_sock[i], - (struct sockaddr *)&his_addr, - &addrlen); - if (fd == -1) { - syslog(LOG_WARNING, - "accept: %m"); - continue; - } - switch (pid = fork()) { - case 0: - /* child */ - (void) dup2(fd, s); - (void) dup2(fd, STDOUT_FILENO); - (void) close(fd); - for (i = 1; i <= *ctl_sock; i++) - close(ctl_sock[i]); - if (pfh != NULL) - pidfile_close(pfh); - goto gotchild; - case -1: - syslog(LOG_WARNING, "fork: %m"); - /* FALLTHROUGH */ - default: - close(fd); - } - } - } - } else { - addrlen = sizeof(his_addr); - if (getpeername(s, (struct sockaddr *)&his_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); - exit(1); - } - -#ifdef VIRTUAL_HOSTING - if (his_addr.su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) - family = AF_INET; - else - family = his_addr.su_family; - inithosts(family); -#endif - } - -gotchild: - sa.sa_handler = SIG_DFL; - (void)sigaction(SIGCHLD, &sa, NULL); - - sa.sa_handler = sigurg; - sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ - (void)sigaction(SIGURG, &sa, NULL); - - sigfillset(&sa.sa_mask); /* block all signals in handler */ - sa.sa_flags = SA_RESTART; - sa.sa_handler = sigquit; - (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); - - addrlen = sizeof(ctrl_addr); - if (getsockname(s, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); - exit(1); - } - dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ -#ifdef VIRTUAL_HOSTING - /* select our identity from virtual host table */ - selecthost(&ctrl_addr); -#endif -#ifdef IP_TOS - if (ctrl_addr.su_family == AF_INET) - { - tos = IPTOS_LOWDELAY; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); - } -#endif - /* - * Disable Nagle on the control channel so that we don't have to wait - * for peer's ACK before issuing our next reply. - */ - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); - - data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); - - (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid()); - - /* Try to handle urgent data inline */ -#ifdef SO_OOBINLINE - if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); -#endif - -#ifdef F_SETOWN - if (fcntl(s, F_SETOWN, getpid()) == -1) - syslog(LOG_ERR, "fcntl F_SETOWN: %m"); -#endif - dolog((struct sockaddr *)&his_addr); - /* - * Set up default state - */ - data = -1; - type = TYPE_A; - form = FORM_N; - stru = STRU_F; - mode = MODE_S; - tmpline[0] = '\0'; - - /* If logins are disabled, print out the message. */ - if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(530, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - reply(530, "System not available."); - exit(0); - } -#ifdef VIRTUAL_HOSTING - fd = fopen(thishost->welcome, "r"); -#else - fd = fopen(_PATH_FTPWELCOME, "r"); -#endif - if (fd != NULL) { - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(220, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - /* reply(220,) must follow */ - } -#ifndef VIRTUAL_HOSTING - if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) - fatalerror("Ran out of memory."); - if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) - hostname[0] = '\0'; - hostname[MAXHOSTNAMELEN - 1] = '\0'; -#endif - if (hostinfo) - reply(220, "%s FTP server (%s) ready.", hostname, version); - else - reply(220, "FTP server ready."); - BLACKLIST_INIT(); - for (;;) - (void) yyparse(); - /* NOTREACHED */ -} - -static void -lostconn(int signo) -{ - - if (ftpdebug) - syslog(LOG_DEBUG, "lost connection"); - dologout(1); -} - -static void -sigquit(int signo) -{ - - syslog(LOG_ERR, "got signal %d", signo); - dologout(1); -} - -#ifdef VIRTUAL_HOSTING -/* - * read in virtual host tables (if they exist) - */ - -static void -inithosts(int family) -{ - int insert; - size_t len; - FILE *fp; - char *cp, *mp, *line; - char *hostname; - char *vhost, *anonuser, *statfile, *welcome, *loginmsg; - struct ftphost *hrp, *lhrp; - struct addrinfo hints, *res, *ai; - - /* - * Fill in the default host information - */ - if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) - fatalerror("Ran out of memory."); - if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) - hostname[0] = '\0'; - hostname[MAXHOSTNAMELEN - 1] = '\0'; - if ((hrp = malloc(sizeof(struct ftphost))) == NULL) - fatalerror("Ran out of memory."); - hrp->hostname = hostname; - hrp->hostinfo = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) - hrp->hostinfo = res; - hrp->statfile = _PATH_FTPDSTATFILE; - hrp->welcome = _PATH_FTPWELCOME; - hrp->loginmsg = _PATH_FTPLOGINMESG; - hrp->anonuser = "ftp"; - hrp->next = NULL; - thishost = firsthost = lhrp = hrp; - if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { - int addrsize, gothost; - void *addr; - struct hostent *hp; - - while ((line = fgetln(fp, &len)) != NULL) { - int i, hp_error; - - /* skip comments */ - if (line[0] == '#') - continue; - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - mp = NULL; - } else { - if ((mp = malloc(len + 1)) == NULL) - fatalerror("Ran out of memory."); - memcpy(mp, line, len); - mp[len] = '\0'; - line = mp; - } - cp = strtok(line, " \t"); - /* skip empty lines */ - if (cp == NULL) - goto nextline; - vhost = cp; - - /* set defaults */ - anonuser = "ftp"; - statfile = _PATH_FTPDSTATFILE; - welcome = _PATH_FTPWELCOME; - loginmsg = _PATH_FTPLOGINMESG; - - /* - * Preparse the line so we can use its info - * for all the addresses associated with - * the virtual host name. - * Field 0, the virtual host name, is special: - * it's already parsed off and will be strdup'ed - * later, after we know its canonical form. - */ - for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) - if (*cp != '-' && (cp = strdup(cp))) - switch (i) { - case 1: /* anon user permissions */ - anonuser = cp; - break; - case 2: /* statistics file */ - statfile = cp; - break; - case 3: /* welcome message */ - welcome = cp; - break; - case 4: /* login message */ - loginmsg = cp; - break; - default: /* programming error */ - abort(); - /* NOTREACHED */ - } - - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(vhost, NULL, &hints, &res) != 0) - goto nextline; - for (ai = res; ai != NULL && ai->ai_addr != NULL; - ai = ai->ai_next) { - - gothost = 0; - for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { - struct addrinfo *hi; - - for (hi = hrp->hostinfo; hi != NULL; - hi = hi->ai_next) - if (hi->ai_addrlen == ai->ai_addrlen && - memcmp(hi->ai_addr, - ai->ai_addr, - ai->ai_addr->sa_len) == 0) { - gothost++; - break; - } - if (gothost) - break; - } - if (hrp == NULL) { - if ((hrp = malloc(sizeof(struct ftphost))) == NULL) - goto nextline; - hrp->hostname = NULL; - insert = 1; - } else { - if (hrp->hostinfo && hrp->hostinfo != res) - freeaddrinfo(hrp->hostinfo); - insert = 0; /* host already in the chain */ - } - hrp->hostinfo = res; - - /* - * determine hostname to use. - * force defined name if there is a valid alias - * otherwise fallback to primary hostname - */ - /* XXX: getaddrinfo() can't do alias check */ - switch(hrp->hostinfo->ai_family) { - case AF_INET: - addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; - addrsize = sizeof(struct in_addr); - break; - case AF_INET6: - addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; - addrsize = sizeof(struct in6_addr); - break; - default: - /* should not reach here */ - freeaddrinfo(hrp->hostinfo); - if (insert) - free(hrp); /*not in chain, can free*/ - else - hrp->hostinfo = NULL; /*mark as blank*/ - goto nextline; - /* NOTREACHED */ - } - if ((hp = getipnodebyaddr(addr, addrsize, - hrp->hostinfo->ai_family, - &hp_error)) != NULL) { - if (strcmp(vhost, hp->h_name) != 0) { - if (hp->h_aliases == NULL) - vhost = hp->h_name; - else { - i = 0; - while (hp->h_aliases[i] && - strcmp(vhost, hp->h_aliases[i]) != 0) - ++i; - if (hp->h_aliases[i] == NULL) - vhost = hp->h_name; - } - } - } - if (hrp->hostname && - strcmp(hrp->hostname, vhost) != 0) { - free(hrp->hostname); - hrp->hostname = NULL; - } - if (hrp->hostname == NULL && - (hrp->hostname = strdup(vhost)) == NULL) { - freeaddrinfo(hrp->hostinfo); - hrp->hostinfo = NULL; /* mark as blank */ - if (hp) - freehostent(hp); - goto nextline; - } - hrp->anonuser = anonuser; - hrp->statfile = statfile; - hrp->welcome = welcome; - hrp->loginmsg = loginmsg; - if (insert) { - hrp->next = NULL; - lhrp->next = hrp; - lhrp = hrp; - } - if (hp) - freehostent(hp); - } -nextline: - if (mp) - free(mp); - } - (void) fclose(fp); - } -} - -static void -selecthost(union sockunion *su) -{ - struct ftphost *hrp; - u_int16_t port; -#ifdef INET6 - struct in6_addr *mapped_in6 = NULL; -#endif - struct addrinfo *hi; - -#ifdef INET6 - /* - * XXX IPv4 mapped IPv6 addr consideraton, - * specified in rfc2373. - */ - if (su->su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) - mapped_in6 = &su->su_sin6.sin6_addr; -#endif - - hrp = thishost = firsthost; /* default */ - port = su->su_port; - su->su_port = 0; - while (hrp != NULL) { - for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { - if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { - thishost = hrp; - goto found; - } -#ifdef INET6 - /* XXX IPv4 mapped IPv6 addr consideraton */ - if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && - (memcmp(&mapped_in6->s6_addr[12], - &((struct sockaddr_in *)hi->ai_addr)->sin_addr, - sizeof(struct in_addr)) == 0)) { - thishost = hrp; - goto found; - } -#endif - } - hrp = hrp->next; - } -found: - su->su_port = port; - /* setup static variables as appropriate */ - hostname = thishost->hostname; - ftpuser = thishost->anonuser; -} -#endif - -/* - * Helper function for sgetpwnam(). - */ -static char * -sgetsave(char *s) -{ - char *new = malloc(strlen(s) + 1); - - if (new == NULL) { - reply(421, "Ran out of memory."); - dologout(1); - /* NOTREACHED */ - } - (void) strcpy(new, s); - return (new); -} - -/* - * 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). - * NB: The data returned by sgetpwnam() will remain valid until - * the next call to this function. Its difference from getpwnam() - * is that sgetpwnam() is known to be called from ftpd code only. - */ -static struct passwd * -sgetpwnam(char *name) -{ - static struct passwd save; - struct passwd *p; - - if ((p = getpwnam(name)) == NULL) - return (p); - if (save.pw_name) { - free(save.pw_name); - free(save.pw_passwd); - free(save.pw_class); - free(save.pw_gecos); - free(save.pw_dir); - free(save.pw_shell); - } - save = *p; - save.pw_name = sgetsave(p->pw_name); - save.pw_passwd = sgetsave(p->pw_passwd); - save.pw_class = sgetsave(p->pw_class); - save.pw_gecos = sgetsave(p->pw_gecos); - save.pw_dir = sgetsave(p->pw_dir); - save.pw_shell = sgetsave(p->pw_shell); - return (&save); -} - -static int login_attempts; /* number of failed login attempts */ -static int askpasswd; /* had user command, ask for passwd */ -static char curname[MAXLOGNAME]; /* current USER name */ - -/* - * 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 - * _PATH_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 - * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. - */ -void -user(char *name) -{ - int ecode; - char *cp, *shell; - - if (logged_in) { - if (guest) { - reply(530, "Can't change user from guest login."); - return; - } else if (dochroot) { - reply(530, "Can't change user from chroot user."); - return; - } - end_login(); - } - - guest = 0; -#ifdef VIRTUAL_HOSTING - pw = sgetpwnam(thishost->anonuser); -#else - pw = sgetpwnam("ftp"); -#endif - if (!noanon && - (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0)) { - if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT)) - reply(530, "User %s access denied.", name); - else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT)) - reply(530, "User %s access denied.", name); - else if (pw != NULL) { - guest = 1; - askpasswd = 1; - reply(331, - "Guest login ok, send your email address as password."); - } else - reply(530, "User %s unknown.", name); - if (!askpasswd && logging) - syslog(LOG_NOTICE, - "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); - return; - } - if (anon_only != 0) { - reply(530, "Sorry, only anonymous ftp allowed."); - return; - } - - if ((pw = sgetpwnam(name))) { - if ((shell = pw->pw_shell) == NULL || *shell == 0) - shell = _PATH_BSHELL; - setusershell(); - while ((cp = getusershell()) != NULL) - if (strcmp(cp, shell) == 0) - break; - endusershell(); - - if (cp == NULL || - (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT))) { - reply(530, "User %s access denied.", name); - if (logging) - syslog(LOG_NOTICE, - "FTP LOGIN REFUSED FROM %s, %s", - remotehost, name); - pw = NULL; - return; - } - } - if (logging) - strlcpy(curname, name, sizeof(curname)); - - reply(331, "Password required for %s.", name); - askpasswd = 1; - /* - * Delay before reading passwd after first failed - * attempt to slow down passwd-guessing programs. - */ - if (login_attempts) - sleep(login_attempts); -} - -/* - * Check if a user is in the file "fname", - * return a pointer to a malloc'd string with the rest - * of the matching line in "residue" if not NULL. - */ -static int -checkuser(char *fname, char *name, int pwset, char **residue, int *ecode) -{ - FILE *fd; - int found = 0; - size_t len; - char *line, *mp, *p; - - if (ecode != NULL) - *ecode = 0; - if ((fd = fopen(fname, "r")) != NULL) { - while (!found && (line = fgetln(fd, &len)) != NULL) { - /* skip comments */ - if (line[0] == '#') - continue; - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - mp = NULL; - } else { - if ((mp = malloc(len + 1)) == NULL) - fatalerror("Ran out of memory."); - memcpy(mp, line, len); - mp[len] = '\0'; - line = mp; - } - /* avoid possible leading and trailing whitespace */ - p = strtok(line, " \t"); - /* skip empty lines */ - if (p == NULL) - goto nextline; - /* - * if first chr is '@', check group membership - */ - if (p[0] == '@') { - int i = 0; - struct group *grp; - - if (p[1] == '\0') /* single @ matches anyone */ - found = 1; - else { - if ((grp = getgrnam(p+1)) == NULL) - goto nextline; - /* - * Check user's default group - */ - if (pwset && grp->gr_gid == pw->pw_gid) - found = 1; - /* - * Check supplementary groups - */ - while (!found && grp->gr_mem[i]) - found = strcmp(name, - grp->gr_mem[i++]) - == 0; - } - } - /* - * Otherwise, just check for username match - */ - else - found = strcmp(p, name) == 0; - /* - * Save the rest of line to "residue" if matched - */ - if (found && residue) { - if ((p = strtok(NULL, "")) != NULL) - p += strspn(p, " \t"); - if (p && *p) { - if ((*residue = strdup(p)) == NULL) - fatalerror("Ran out of memory."); - } else - *residue = NULL; - } -nextline: - if (mp) - free(mp); - } - (void) fclose(fd); - } else if (ecode != NULL) - *ecode = errno; - return (found); -} - -/* - * 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 - - (void) seteuid(0); -#ifdef LOGIN_CAP - setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | - LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | - LOGIN_SETENV)); -#endif - if (logged_in && dowtmp) - ftpd_logwtmp(wtmpid, NULL, NULL); - pw = NULL; -#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 - logged_in = 0; - guest = 0; - dochroot = 0; -} - -#ifdef USE_PAM - -/* - * the following code is stolen from imap-uw PAM authentication module and - * login.c - */ -#define COPY_STRING(s) (s ? strdup(s) : NULL) - -struct cred_t { - const char *uname; /* user name */ - const char *pass; /* password */ -}; -typedef struct cred_t cred_t; - -static int -auth_conv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata) -{ - int i; - cred_t *cred = (cred_t *) appdata; - struct pam_response *reply; - - reply = calloc(num_msg, sizeof *reply); - if (reply == NULL) - return PAM_BUF_ERR; - - for (i = 0; i < num_msg; i++) { - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_ON: /* assume want user name */ - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = COPY_STRING(cred->uname); - /* PAM frees resp. */ - break; - case PAM_PROMPT_ECHO_OFF: /* assume want password */ - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = COPY_STRING(cred->pass); - /* PAM frees resp. */ - break; - case PAM_TEXT_INFO: - case PAM_ERROR_MSG: - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = NULL; - break; - default: /* unknown message style */ - free(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - return PAM_SUCCESS; -} - -/* - * 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. - */ -static int -auth_pam(struct passwd **ppw, const char *pass) -{ - const char *tmpl_user; - const void *item; - int rval; - int e; - cred_t auth_cred = { (*ppw)->pw_name, pass }; - struct pam_conv conv = { &auth_conv, &auth_cred }; - - e = pam_start("ftpd", (*ppw)->pw_name, &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_authenticate(pamh, 0); - 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 (strcmp((*ppw)->pw_name, tmpl_user) != 0) - *ppw = getpwnam(tmpl_user); - } 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 */ - -void -pass(char *passwd) -{ - int rval, ecode; - FILE *fd; -#ifdef LOGIN_CAP - login_cap_t *lc = NULL; -#endif -#ifdef USE_PAM - int e; -#endif - char *residue = NULL; - char *xpasswd; - - if (logged_in || askpasswd == 0) { - reply(503, "Login with USER first."); - return; - } - askpasswd = 0; - if (!guest) { /* "ftp" is only account allowed no password */ - if (pw == NULL) { - rval = 1; /* failure below */ - goto skip; - } -#ifdef USE_PAM - rval = auth_pam(&pw, passwd); - if (rval >= 0) { - goto skip; - } -#endif - xpasswd = crypt(passwd, pw->pw_passwd); - if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') - xpasswd = ":"; - rval = strcmp(pw->pw_passwd, xpasswd); - if (pw->pw_expire && time(NULL) >= pw->pw_expire) - rval = 1; /* failure */ -skip: - /* - * If rval == 1, the user failed the authentication check - * above. If rval == 0, either PAM or local authentication - * succeeded. - */ - if (rval) { - reply(530, "Login incorrect."); - BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, STDIN_FILENO, "Login incorrect"); - if (logging) { - syslog(LOG_NOTICE, - "FTP LOGIN FAILED FROM %s", - remotehost); - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "FTP LOGIN FAILED FROM %s, %s", - remotehost, curname); - } - pw = NULL; - if (login_attempts++ >= 5) { - syslog(LOG_NOTICE, - "repeated login failures from %s", - remotehost); - exit(0); - } - return; - } else { - BLACKLIST_NOTIFY(BLACKLIST_AUTH_OK, STDIN_FILENO, "Login successful"); - } - } - login_attempts = 0; /* this time successful */ - if (setegid(pw->pw_gid) < 0) { - reply(550, "Can't set gid."); - return; - } - /* May be overridden by login.conf */ - (void) umask(defumask); -#ifdef LOGIN_CAP - if ((lc = login_getpwclass(pw)) != NULL) { - char remote_ip[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; - } - } - setusercontext(lc, pw, 0, LOGIN_SETALL & - ~(LOGIN_SETRESOURCES | LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV)); -#else - setlogin(pw->pw_name); - (void) initgroups(pw->pw_name, pw->pw_gid); -#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 - - dochroot = - checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode) -#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ - || login_getcapbool(lc, "ftp-chroot", 0) -#endif - ; - /* - * It is possible that checkuser() failed to open the chroot file. - * If this is the case, report that logins are un-available, since we - * have no way of checking whether or not the user should be chrooted. - * We ignore ENOENT since it is not required that this file be present. - */ - if (ecode != 0 && ecode != ENOENT) { - reply(530, "Login not available right now."); - return; - } - chrootdir = NULL; - - /* Disable wtmp logging when chrooting. */ - if (dochroot || guest) - dowtmp = 0; - if (dowtmp) - ftpd_logwtmp(wtmpid, pw->pw_name, - (struct sockaddr *)&his_addr); - logged_in = 1; - -#ifdef LOGIN_CAP - setusercontext(lc, pw, 0, LOGIN_SETRESOURCES); -#endif - - if (guest && stats && statfd < 0) { -#ifdef VIRTUAL_HOSTING - statfd = open(thishost->statfile, O_WRONLY|O_APPEND); -#else - statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); -#endif - if (statfd < 0) - stats = 0; - } - - /* - * For a chrooted local user, - * a) see whether ftpchroot(5) specifies a chroot directory, - * b) extract the directory pathname from the line, - * c) expand it to the absolute pathname if necessary. - */ - if (dochroot && residue && - (chrootdir = strtok(residue, " \t")) != NULL) { - if (chrootdir[0] != '/') - asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); - else - chrootdir = strdup(chrootdir); /* make it permanent */ - if (chrootdir == NULL) - fatalerror("Ran out of memory."); - } - if (guest || dochroot) { - /* - * If no chroot directory set yet, use the login directory. - * Copy it so it can be modified while pw->pw_dir stays intact. - */ - if (chrootdir == NULL && - (chrootdir = strdup(pw->pw_dir)) == NULL) - fatalerror("Ran out of memory."); - /* - * Check for the "/chroot/./home" syntax, - * separate the chroot and home directory pathnames. - */ - if ((homedir = strstr(chrootdir, "/./")) != NULL) { - *(homedir++) = '\0'; /* wipe '/' */ - homedir++; /* skip '.' */ - } else { - /* - * We MUST do a chdir() after the chroot. Otherwise - * the old current directory will be accessible as "." - * outside the new root! - */ - homedir = "/"; - } - /* - * Finally, do chroot() - */ - if (chroot(chrootdir) < 0) { - reply(550, "Can't change root."); - goto bad; - } - __FreeBSD_libc_enter_restricted_mode(); - } else /* real user w/o chroot */ - homedir = pw->pw_dir; - /* - * Set euid *before* doing chdir() so - * a) the user won't be carried to a directory that he couldn't reach - * on his own due to no permission to upper path components, - * b) NFS mounted homedirs w/restrictive permissions will be accessible - * (uid 0 has no root power over NFS if not mapped explicitly.) - */ - if (seteuid(pw->pw_uid) < 0) { - if (guest || dochroot) { - fatalerror("Can't set uid."); - } else { - reply(550, "Can't set uid."); - goto bad; - } - } - /* - * Do not allow the session to live if we're chroot()'ed and chdir() - * fails. Otherwise the chroot jail can be escaped. - */ - if (chdir(homedir) < 0) { - if (guest || dochroot) { - fatalerror("Can't change to base directory."); - } else { - if (chdir("/") < 0) { - reply(550, "Root is inaccessible."); - goto bad; - } - lreply(230, "No directory! Logging in with home=/."); - } - } - - /* - * Display a login message, if it exists. - * N.B. reply(230,) must follow the message. - */ -#ifdef VIRTUAL_HOSTING - fd = fopen(thishost->loginmsg, "r"); -#else - fd = fopen(_PATH_FTPLOGINMESG, "r"); -#endif - if (fd != NULL) { - char *cp, line[LINE_MAX]; - - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(230, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - } - if (guest) { - if (ident != NULL) - free(ident); - ident = strdup(passwd); - if (ident == NULL) - fatalerror("Ran out of memory."); - - reply(230, "Guest login ok, access restrictions apply."); -#ifdef SETPROCTITLE -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous(%s)/%s", remotehost, hostname, - passwd); - else -#endif - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous/%s", remotehost, passwd); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", - remotehost, passwd); - } else { - if (dochroot) - reply(230, "User %s logged in, " - "access restrictions apply.", pw->pw_name); - else - reply(230, "User %s logged in.", pw->pw_name); - -#ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: user/%s", remotehost, pw->pw_name); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", - remotehost, pw->pw_name); - } - if (logging && (guest || dochroot)) - syslog(LOG_INFO, "session root changed to %s", chrootdir); -#ifdef LOGIN_CAP - login_close(lc); -#endif - if (residue) - free(residue); - return; -bad: - /* Forget all about it... */ -#ifdef LOGIN_CAP - login_close(lc); -#endif - if (residue) - free(residue); - end_login(); -} - -void -retrieve(char *cmd, char *name) -{ - FILE *fin, *dout; - struct stat st; - int (*closefunc)(FILE *); - time_t start; - char line[BUFSIZ]; - - if (cmd == 0) { - fin = fopen(name, "r"), closefunc = fclose; - st.st_size = 0; - } else { - (void) snprintf(line, sizeof(line), cmd, name); - name = line; - fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; - st.st_size = -1; - st.st_blksize = BUFSIZ; - } - if (fin == NULL) { - if (errno != 0) { - perror_reply(550, name); - if (cmd == 0) { - LOGCMD("get", name); - } - } - return; - } - byte_count = -1; - if (cmd == 0) { - if (fstat(fileno(fin), &st) < 0) { - perror_reply(550, name); - goto done; - } - if (!S_ISREG(st.st_mode)) { - /* - * Never sending a raw directory is a workaround - * for buggy clients that will attempt to RETR - * a directory before listing it, e.g., Mozilla. - * Preventing a guest from getting irregular files - * is a simple security measure. - */ - if (S_ISDIR(st.st_mode) || guest) { - reply(550, "%s: not a plain file.", name); - goto done; - } - st.st_size = -1; - /* st.st_blksize is set for all descriptor types */ - } - } - if (restart_point) { - if (type == TYPE_A) { - off_t i, n; - int c; - - n = restart_point; - i = 0; - while (i++ < n) { - if ((c=getc(fin)) == EOF) { - perror_reply(550, name); - goto done; - } - if (c == '\n') - i++; - } - } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { - perror_reply(550, name); - goto done; - } - } - dout = dataconn(name, st.st_size, "w"); - if (dout == NULL) - goto done; - time(&start); - send_data(fin, dout, st.st_blksize, st.st_size, - restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); - if (cmd == 0 && guest && stats && byte_count > 0) - logxfer(name, byte_count, start); - (void) fclose(dout); - data = -1; - pdata = -1; -done: - if (cmd == 0) - LOGBYTES("get", name, byte_count); - (*closefunc)(fin); -} - -void -store(char *name, char *mode, int unique) -{ - int fd; - FILE *fout, *din; - int (*closefunc)(FILE *); - - if (*mode == 'a') { /* APPE */ - if (unique) { - /* Programming error */ - syslog(LOG_ERR, "Internal: unique flag to APPE"); - unique = 0; - } - if (guest && noguestmod) { - reply(550, "Appending to existing file denied."); - goto err; - } - restart_point = 0; /* not affected by preceding REST */ - } - if (unique) /* STOU overrides REST */ - restart_point = 0; - if (guest && noguestmod) { - if (restart_point) { /* guest STOR w/REST */ - reply(550, "Modifying existing file denied."); - goto err; - } else /* treat guest STOR as STOU */ - unique = 1; - } - - if (restart_point) - mode = "r+"; /* so ASCII manual seek can work */ - if (unique) { - if ((fd = guniquefd(name, &name)) < 0) - goto err; - fout = fdopen(fd, mode); - } else - fout = fopen(name, mode); - closefunc = fclose; - if (fout == NULL) { - perror_reply(553, name); - goto err; - } - byte_count = -1; - if (restart_point) { - if (type == TYPE_A) { - off_t i, n; - int c; - - n = restart_point; - i = 0; - while (i++ < n) { - if ((c=getc(fout)) == EOF) { - 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 (fseeko(fout, 0, SEEK_CUR) < 0) { - perror_reply(550, name); - goto done; - } - } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { - perror_reply(550, name); - goto done; - } - } - din = dataconn(name, -1, "r"); - if (din == NULL) - goto done; - if (receive_data(din, fout) == 0) { - if (unique) - reply(226, "Transfer complete (unique file name:%s).", - name); - else - reply(226, "Transfer complete."); - } - (void) fclose(din); - data = -1; - pdata = -1; -done: - LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); - (*closefunc)(fout); - return; -err: - LOGCMD(*mode == 'a' ? "append" : "put" , name); - return; -} - -static FILE * -getdatasock(char *mode) -{ - int on = 1, s, t, tries; - - if (data >= 0) - return (fdopen(data, mode)); - - s = socket(data_dest.su_family, SOCK_STREAM, 0); - if (s < 0) - goto bad; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); - /* anchor socket to avoid multi-homing problems */ - data_source = ctrl_addr; - data_source.su_port = htons(dataport); - (void) seteuid(0); - for (tries = 1; ; tries++) { - /* - * We should loop here since it's possible that - * another ftpd instance has passed this point and is - * trying to open a data connection in active mode now. - * Until the other connection is opened, we'll be getting - * EADDRINUSE because no SOCK_STREAM sockets in the system - * can share both local and remote addresses, localIP:20 - * and *:* in this case. - */ - if (bind(s, (struct sockaddr *)&data_source, - data_source.su_len) >= 0) - break; - if (errno != EADDRINUSE || tries > 10) - goto bad; - sleep(tries); - } - (void) seteuid(pw->pw_uid); -#ifdef IP_TOS - if (data_source.su_family == AF_INET) - { - on = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) - syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); - } -#endif -#ifdef TCP_NOPUSH - /* - * Turn off push flag to keep sender TCP from sending short packets - * at the boundaries of each write(). - */ - on = 1; - if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) - syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); -#endif - return (fdopen(s, mode)); -bad: - /* Return the real value of errno (close may change it) */ - t = errno; - (void) seteuid(pw->pw_uid); - (void) close(s); - errno = t; - return (NULL); -} - -static FILE * -dataconn(char *name, off_t size, char *mode) -{ - char sizebuf[32]; - FILE *file; - int retry = 0, tos, conerrno; - - file_size = size; - byte_count = 0; - if (size != -1) - (void) snprintf(sizebuf, sizeof(sizebuf), - " (%jd bytes)", (intmax_t)size); - else - *sizebuf = '\0'; - if (pdata >= 0) { - union sockunion from; - socklen_t fromlen = ctrl_addr.su_len; - int flags, s; - struct timeval timeout; - fd_set set; - - FD_ZERO(&set); - FD_SET(pdata, &set); - - timeout.tv_usec = 0; - timeout.tv_sec = 120; - - /* - * Granted a socket is in the blocking I/O mode, - * accept() will block after a successful select() - * if the selected connection dies in between. - * Therefore set the non-blocking I/O flag here. - */ - if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || - fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) - goto pdata_err; - if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || - (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) - goto pdata_err; - (void) close(pdata); - pdata = s; - /* - * Unset the inherited non-blocking I/O flag - * on the child socket so stdio can work on it. - */ - if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || - fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) - goto pdata_err; -#ifdef IP_TOS - if (from.su_family == AF_INET) - { - tos = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); - } -#endif - reply(150, "Opening %s mode data connection for '%s'%s.", - type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); - return (fdopen(pdata, mode)); -pdata_err: - reply(425, "Can't open data connection."); - (void) close(pdata); - pdata = -1; - return (NULL); - } - if (data >= 0) { - reply(125, "Using existing data connection for '%s'%s.", - name, sizebuf); - usedefault = 1; - return (fdopen(data, mode)); - } - if (usedefault) - data_dest = his_addr; - usedefault = 1; - do { - file = getdatasock(mode); - if (file == NULL) { - char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; - - if (getnameinfo((struct sockaddr *)&data_source, - data_source.su_len, - hostbuf, sizeof(hostbuf) - 1, - portbuf, sizeof(portbuf) - 1, - NI_NUMERICHOST|NI_NUMERICSERV)) - *hostbuf = *portbuf = 0; - hostbuf[sizeof(hostbuf) - 1] = 0; - portbuf[sizeof(portbuf) - 1] = 0; - reply(425, "Can't create data socket (%s,%s): %s.", - hostbuf, portbuf, strerror(errno)); - return (NULL); - } - data = fileno(file); - conerrno = 0; - if (connect(data, (struct sockaddr *)&data_dest, - data_dest.su_len) == 0) - break; - conerrno = errno; - (void) fclose(file); - data = -1; - if (conerrno == EADDRINUSE) { - sleep(swaitint); - retry += swaitint; - } else { - break; - } - } while (retry <= swaitmax); - if (conerrno != 0) { - reply(425, "Can't build data connection: %s.", - strerror(conerrno)); - return (NULL); - } - reply(150, "Opening %s mode data connection for '%s'%s.", - type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); - return (file); -} - -/* - * A helper macro to avoid code duplication - * in send_data() and receive_data(). - * - * XXX We have to block SIGURG during putc() because BSD stdio - * is unable to restart interrupted write operations and hence - * the entire buffer contents will be lost as soon as a write() - * call indicates EINTR to stdio. - */ -#define FTPD_PUTC(ch, file, label) \ - do { \ - int ret; \ - \ - do { \ - START_UNSAFE; \ - ret = putc((ch), (file)); \ - END_UNSAFE; \ - CHECKOOB(return (-1)) \ - else if (ferror(file)) \ - goto label; \ - clearerr(file); \ - } while (ret == EOF); \ - } while (0) - -/* - * 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, size_t blksize, off_t filesize, int isreg) -{ - int c, cp, filefd, netfd; - char *buf; - - STARTXFER; - - switch (type) { - - case TYPE_A: - cp = EOF; - for (;;) { - c = getc(instr); - CHECKOOB(return (-1)) - else if (c == EOF && ferror(instr)) - goto file_err; - if (c == EOF) { - if (ferror(instr)) { /* resume after OOB */ - clearerr(instr); - continue; - } - if (feof(instr)) /* EOF */ - break; - syslog(LOG_ERR, "Internal: impossible condition" - " on file after getc()"); - goto file_err; - } - if (c == '\n' && cp != '\r') { - FTPD_PUTC('\r', outstr, data_err); - byte_count++; - } - FTPD_PUTC(c, outstr, data_err); - byte_count++; - cp = c; - } -#ifdef notyet /* BSD stdio isn't ready for that */ - while (fflush(outstr) == EOF) { - CHECKOOB(return (-1)) - else - goto data_err; - clearerr(outstr); - } - ENDXFER; -#else - ENDXFER; - if (fflush(outstr) == EOF) - goto data_err; -#endif - reply(226, "Transfer complete."); - return (0); - - case TYPE_I: - case TYPE_L: - /* - * isreg is only set if we are not doing restart and we - * are sending a regular file - */ - netfd = fileno(outstr); - filefd = fileno(instr); - - if (isreg) { - char *msg = "Transfer complete."; - off_t cnt, offset; - int err; - - cnt = offset = 0; - - while (filesize > 0) { - err = sendfile(filefd, netfd, offset, 0, - NULL, &cnt, 0); - /* - * Calculate byte_count before OOB processing. - * It can be used in myoob() later. - */ - byte_count += cnt; - offset += cnt; - filesize -= cnt; - CHECKOOB(return (-1)) - else if (err == -1) { - if (errno != EINTR && - cnt == 0 && offset == 0) - goto oldway; - goto data_err; - } - if (err == -1) /* resume after OOB */ - continue; - /* - * We hit the EOF prematurely. - * Perhaps the file was externally truncated. - */ - if (cnt == 0) { - msg = "Transfer finished due to " - "premature end of file."; - break; - } - } - ENDXFER; - reply(226, "%s", msg); - return (0); - } - -oldway: - if ((buf = malloc(blksize)) == NULL) { - ENDXFER; - reply(451, "Ran out of memory."); - return (-1); - } - - for (;;) { - int cnt, len; - char *bp; - - cnt = read(filefd, buf, blksize); - CHECKOOB(free(buf); return (-1)) - else if (cnt < 0) { - free(buf); - goto file_err; - } - if (cnt < 0) /* resume after OOB */ - continue; - if (cnt == 0) /* EOF */ - break; - for (len = cnt, bp = buf; len > 0;) { - cnt = write(netfd, bp, len); - CHECKOOB(free(buf); return (-1)) - else if (cnt < 0) { - free(buf); - goto data_err; - } - if (cnt <= 0) - continue; - len -= cnt; - bp += cnt; - byte_count += cnt; - } - } - ENDXFER; - free(buf); - reply(226, "Transfer complete."); - return (0); - default: - ENDXFER; - reply(550, "Unimplemented TYPE %d in send_data.", type); - return (-1); - } - -data_err: - ENDXFER; - perror_reply(426, "Data connection"); - return (-1); - -file_err: - ENDXFER; - perror_reply(551, "Error on input file"); - return (-1); -} - -/* - * 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, cp; - int bare_lfs = 0; - - STARTXFER; - - switch (type) { - - case TYPE_I: - case TYPE_L: - for (;;) { - int cnt, len; - char *bp; - char buf[BUFSIZ]; - - cnt = read(fileno(instr), buf, sizeof(buf)); - CHECKOOB(return (-1)) - else if (cnt < 0) - goto data_err; - if (cnt < 0) /* resume after OOB */ - continue; - if (cnt == 0) /* EOF */ - break; - for (len = cnt, bp = buf; len > 0;) { - cnt = write(fileno(outstr), bp, len); - CHECKOOB(return (-1)) - else if (cnt < 0) - goto file_err; - if (cnt <= 0) - continue; - len -= cnt; - bp += cnt; - byte_count += cnt; - } - } - ENDXFER; - return (0); - - case TYPE_E: - ENDXFER; - reply(553, "TYPE E not implemented."); - return (-1); - - case TYPE_A: - cp = EOF; - for (;;) { - c = getc(instr); - CHECKOOB(return (-1)) - else if (c == EOF && ferror(instr)) - goto data_err; - if (c == EOF && ferror(instr)) { /* resume after OOB */ - clearerr(instr); - continue; - } - - if (cp == '\r') { - if (c != '\n') - FTPD_PUTC('\r', outstr, file_err); - } else - if (c == '\n') - bare_lfs++; - if (c == '\r') { - byte_count++; - cp = c; - continue; - } - - /* Check for EOF here in order not to lose last \r. */ - if (c == EOF) { - if (feof(instr)) /* EOF */ - break; - syslog(LOG_ERR, "Internal: impossible condition" - " on data stream after getc()"); - goto data_err; - } - - byte_count++; - FTPD_PUTC(c, outstr, file_err); - cp = c; - } -#ifdef notyet /* BSD stdio isn't ready for that */ - while (fflush(outstr) == EOF) { - CHECKOOB(return (-1)) - else - goto file_err; - clearerr(outstr); - } - ENDXFER; -#else - ENDXFER; - if (fflush(outstr) == EOF) - goto file_err; -#endif - if (bare_lfs) { - lreply(226, - "WARNING! %d bare linefeeds received in ASCII mode.", - bare_lfs); - (void)printf(" File may not have transferred correctly.\r\n"); - } - return (0); - default: - ENDXFER; - reply(550, "Unimplemented TYPE %d in receive_data.", type); - return (-1); - } - -data_err: - ENDXFER; - perror_reply(426, "Data connection"); - return (-1); - -file_err: - ENDXFER; - perror_reply(452, "Error writing to file"); - return (-1); -} - -void -statfilecmd(char *filename) -{ - FILE *fin; - int atstart; - int c, code; - char line[LINE_MAX]; - struct stat st; - - code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; - (void)snprintf(line, sizeof(line), _PATH_LS " -lA %s", filename); - fin = ftpd_popen(line, "r"); - if (fin == NULL) { - perror_reply(551, filename); - return; - } - lreply(code, "Status of %s:", filename); - 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; - } - (void) putc('\r', stdout); - } - /* - * RFC 959 says neutral text should be prepended before - * a leading 3-digit number followed by whitespace, but - * many ftp clients can be confused by any leading digits, - * as a matter of fact. - */ - if (atstart && isdigit(c)) - (void) putc(' ', stdout); - (void) putc(c, stdout); - atstart = (c == '\n'); - } - (void) ftpd_pclose(fin); - reply(code, "End of status."); -} - -void -statcmd(void) -{ - union sockunion *su; - u_char *a, *p; - char hname[NI_MAXHOST]; - int ispassive; - - if (hostinfo) { - lreply(211, "%s FTP server status:", hostname); - printf(" %s\r\n", version); - } else - lreply(211, "FTP server status:"); - printf(" Connected to %s", remotehost); - if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, - hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { - hname[sizeof(hname) - 1] = 0; - if (strcmp(hname, remotehost) != 0) - printf(" (%s)", hname); - } - printf("\r\n"); - if (logged_in) { - if (guest) - printf(" Logged in anonymously\r\n"); - else - printf(" Logged in as %s\r\n", pw->pw_name); - } else if (askpasswd) - printf(" Waiting for password\r\n"); - else - printf(" Waiting for user name\r\n"); - printf(" TYPE: %s", typenames[type]); - if (type == TYPE_A || type == TYPE_E) - printf(", FORM: %s", formnames[form]); - if (type == TYPE_L) -#if CHAR_BIT == 8 - printf(" %d", CHAR_BIT); -#else - printf(" %d", bytesize); /* need definition! */ -#endif - printf("; STRUcture: %s; transfer MODE: %s\r\n", - strunames[stru], modenames[mode]); - if (data != -1) - printf(" Data connection open\r\n"); - else if (pdata != -1) { - ispassive = 1; - su = &pasv_addr; - goto printaddr; - } else if (usedefault == 0) { - ispassive = 0; - su = &data_dest; -printaddr: -#define UC(b) (((int) b) & 0xff) - if (epsvall) { - printf(" EPSV only mode (EPSV ALL)\r\n"); - goto epsvonly; - } - - /* PORT/PASV */ - if (su->su_family == AF_INET) { - a = (u_char *) &su->su_sin.sin_addr; - p = (u_char *) &su->su_sin.sin_port; - printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", - ispassive ? "PASV" : "PORT", - UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(p[0]), UC(p[1])); - } - - /* LPRT/LPSV */ - { - int alen, af, i; - - switch (su->su_family) { - case AF_INET: - a = (u_char *) &su->su_sin.sin_addr; - p = (u_char *) &su->su_sin.sin_port; - alen = sizeof(su->su_sin.sin_addr); - af = 4; - break; - case AF_INET6: - a = (u_char *) &su->su_sin6.sin6_addr; - p = (u_char *) &su->su_sin6.sin6_port; - alen = sizeof(su->su_sin6.sin6_addr); - af = 6; - break; - default: - af = 0; - break; - } - if (af) { - printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", - af, alen); - for (i = 0; i < alen; i++) - printf("%d,", UC(a[i])); - printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); - } - } - -epsvonly:; - /* EPRT/EPSV */ - { - int af; - - switch (su->su_family) { - case AF_INET: - af = 1; - break; - case AF_INET6: - af = 2; - break; - default: - af = 0; - break; - } - if (af) { - union sockunion tmp; - - tmp = *su; - if (tmp.su_family == AF_INET6) - tmp.su_sin6.sin6_scope_id = 0; - if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, - hname, sizeof(hname) - 1, NULL, 0, - NI_NUMERICHOST)) { - hname[sizeof(hname) - 1] = 0; - printf(" %s |%d|%s|%d|\r\n", - ispassive ? "EPSV" : "EPRT", - af, hname, htons(tmp.su_port)); - } - } - } -#undef UC - } else - printf(" No data connection\r\n"); - reply(211, "End of status."); -} - -void -fatalerror(char *s) -{ - - reply(451, "Error in server: %s", s); - reply(221, "Closing connection due to server error."); - dologout(0); - /* NOTREACHED */ -} - -void -reply(int n, const char *fmt, ...) -{ - va_list ap; - - (void)printf("%d ", n); - va_start(ap, fmt); - (void)vprintf(fmt, ap); - va_end(ap); - (void)printf("\r\n"); - (void)fflush(stdout); - if (ftpdebug) { - syslog(LOG_DEBUG, "<--- %d ", n); - va_start(ap, fmt); - vsyslog(LOG_DEBUG, fmt, ap); - va_end(ap); - } -} - -void -lreply(int n, const char *fmt, ...) -{ - va_list ap; - - (void)printf("%d- ", n); - va_start(ap, fmt); - (void)vprintf(fmt, ap); - va_end(ap); - (void)printf("\r\n"); - (void)fflush(stdout); - if (ftpdebug) { - syslog(LOG_DEBUG, "<--- %d- ", n); - va_start(ap, fmt); - vsyslog(LOG_DEBUG, fmt, ap); - va_end(ap); - } -} - -static void -ack(char *s) -{ - - reply(250, "%s command successful.", s); -} - -void -nack(char *s) -{ - - reply(502, "%s command not implemented.", s); -} - -/* ARGSUSED */ -void -yyerror(char *s) -{ - char *cp; - - if ((cp = strchr(cbuf,'\n'))) - *cp = '\0'; - reply(500, "%s: command not understood.", cbuf); -} - -void -delete(char *name) -{ - struct stat st; - - LOGCMD("delete", name); - if (lstat(name, &st) < 0) { - perror_reply(550, name); - return; - } - if (S_ISDIR(st.st_mode)) { - if (rmdir(name) < 0) { - perror_reply(550, name); - return; - } - goto done; - } - if (guest && noguestmod) { - reply(550, "Operation not permitted."); - return; - } - if (unlink(name) < 0) { - perror_reply(550, name); - return; - } -done: - ack("DELE"); -} - -void -cwd(char *path) -{ - - if (chdir(path) < 0) - perror_reply(550, path); - else - ack("CWD"); -} - -void -makedir(char *name) -{ - char *s; - - LOGCMD("mkdir", name); - if (guest && noguestmkd) - reply(550, "Operation not permitted."); - else if (mkdir(name, 0777) < 0) - perror_reply(550, name); - else { - if ((s = doublequote(name)) == NULL) - fatalerror("Ran out of memory."); - reply(257, "\"%s\" directory created.", s); - free(s); - } -} - -void -removedir(char *name) -{ - - LOGCMD("rmdir", name); - if (rmdir(name) < 0) - perror_reply(550, name); - else - ack("RMD"); -} - -void -pwd(void) -{ - char *s, path[MAXPATHLEN + 1]; - - if (getcwd(path, sizeof(path)) == NULL) - perror_reply(550, "Get current directory"); - else { - if ((s = doublequote(path)) == NULL) - fatalerror("Ran out of memory."); - reply(257, "\"%s\" is current directory.", s); - free(s); - } -} - -char * -renamefrom(char *name) -{ - struct stat st; - - if (guest && noguestmod) { - reply(550, "Operation not permitted."); - return (NULL); - } - if (lstat(name, &st) < 0) { - perror_reply(550, name); - return (NULL); - } - reply(350, "File exists, ready for destination name."); - return (name); -} - -void -renamecmd(char *from, char *to) -{ - struct stat st; - - LOGCMD2("rename", from, to); - - if (guest && (stat(to, &st) == 0)) { - reply(550, "%s: permission denied.", to); - return; - } - - if (rename(from, to) < 0) - perror_reply(550, "rename"); - else - ack("RNTO"); -} - -static void -dolog(struct sockaddr *who) -{ - char who_name[NI_MAXHOST]; - - realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); - remotehost[sizeof(remotehost) - 1] = 0; - if (getnameinfo(who, who->sa_len, - who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) - *who_name = 0; - who_name[sizeof(who_name) - 1] = 0; - -#ifdef SETPROCTITLE -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", - remotehost, hostname); - else -#endif - snprintf(proctitle, sizeof(proctitle), "%s: connected", - remotehost); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - - if (logging) { -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - syslog(LOG_INFO, "connection from %s (%s) to %s", - remotehost, who_name, hostname); - else -#endif - syslog(LOG_INFO, "connection from %s (%s)", - remotehost, who_name); - } -} - -/* - * Record logout in wtmp file - * and exit with supplied status. - */ -void -dologout(int status) -{ - - if (logged_in && dowtmp) { - (void) seteuid(0); -#ifdef LOGIN_CAP - setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | - LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | - LOGIN_SETENV)); -#endif - ftpd_logwtmp(wtmpid, NULL, NULL); - } - /* beware of flushing buffers after a SIGPIPE */ - _exit(status); -} - -static void -sigurg(int signo) -{ - - recvurg = 1; -} - -static void -maskurg(int flag) -{ - int oerrno; - sigset_t sset; - - if (!transflag) { - syslog(LOG_ERR, "Internal: maskurg() while no transfer"); - return; - } - oerrno = errno; - sigemptyset(&sset); - sigaddset(&sset, SIGURG); - sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); - errno = oerrno; -} - -static void -flagxfer(int flag) -{ - - if (flag) { - if (transflag) - syslog(LOG_ERR, "Internal: flagxfer(1): " - "transfer already under way"); - transflag = 1; - maskurg(0); - recvurg = 0; - } else { - if (!transflag) - syslog(LOG_ERR, "Internal: flagxfer(0): " - "no active transfer"); - maskurg(1); - transflag = 0; - } -} - -/* - * Returns 0 if OK to resume or -1 if abort requested. - */ -static int -myoob(void) -{ - char *cp; - int ret; - - if (!transflag) { - syslog(LOG_ERR, "Internal: myoob() while no transfer"); - return (0); - } - cp = tmpline; - ret = get_line(cp, 7, stdin); - if (ret == -1) { - reply(221, "You could at least say goodbye."); - dologout(0); - } else if (ret == -2) { - /* Ignore truncated command. */ - return (0); - } - upper(cp); - if (strcmp(cp, "ABOR\r\n") == 0) { - tmpline[0] = '\0'; - reply(426, "Transfer aborted. Data connection closed."); - reply(226, "Abort successful."); - return (-1); - } - if (strcmp(cp, "STAT\r\n") == 0) { - tmpline[0] = '\0'; - if (file_size != -1) - reply(213, "Status: %jd of %jd bytes transferred.", - (intmax_t)byte_count, (intmax_t)file_size); - else - reply(213, "Status: %jd bytes transferred.", - (intmax_t)byte_count); - } - 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 on; - char *p, *a; - - if (pdata >= 0) /* close old port if one set */ - close(pdata); - - pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); - if (pdata < 0) { - perror_reply(425, "Can't open passive connection"); - return; - } - on = 1; - if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); - - (void) seteuid(0); - -#ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - on = restricted_data_ports ? IP_PORTRANGE_HIGH - : IP_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif -#ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - on = restricted_data_ports ? IPV6_PORTRANGE_HIGH - : IPV6_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif - - pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) - goto pasv_error; - - (void) seteuid(pw->pw_uid); - - len = sizeof(pasv_addr); - if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) - goto pasv_error; - if (listen(pdata, 1) < 0) - goto pasv_error; - if (pasv_addr.su_family == AF_INET) - a = (char *) &pasv_addr.su_sin.sin_addr; - else if (pasv_addr.su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) - a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; - else - goto pasv_error; - - 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) seteuid(pw->pw_uid); - (void) close(pdata); - pdata = -1; - perror_reply(425, "Can't open passive connection"); - return; -} - -/* - * Long Passive defined in RFC 1639. - * 228 Entering Long Passive Mode - * (af, hal, h1, h2, h3,..., pal, p1, p2...) - */ - -void -long_passive(char *cmd, int pf) -{ - socklen_t len; - int on; - char *p, *a; - - if (pdata >= 0) /* close old port if one set */ - close(pdata); - - if (pf != PF_UNSPEC) { - if (ctrl_addr.su_family != pf) { - switch (ctrl_addr.su_family) { - case AF_INET: - pf = 1; - break; - case AF_INET6: - pf = 2; - break; - default: - pf = 0; - break; - } - /* - * XXX - * only EPRT/EPSV ready clients will understand this - */ - if (strcmp(cmd, "EPSV") == 0 && pf) { - reply(522, "Network protocol mismatch, " - "use (%d)", pf); - } else - reply(501, "Network protocol mismatch."); /*XXX*/ - - return; - } - } - - pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); - if (pdata < 0) { - perror_reply(425, "Can't open passive connection"); - return; - } - on = 1; - if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); - - (void) seteuid(0); - - pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - len = pasv_addr.su_len; - -#ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - on = restricted_data_ports ? IP_PORTRANGE_HIGH - : IP_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif -#ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - on = restricted_data_ports ? IPV6_PORTRANGE_HIGH - : IPV6_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif - - if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) - goto pasv_error; - - (void) seteuid(pw->pw_uid); - - if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) - goto pasv_error; - if (listen(pdata, 1) < 0) - goto pasv_error; - -#define UC(b) (((int) b) & 0xff) - - if (strcmp(cmd, "LPSV") == 0) { - p = (char *)&pasv_addr.su_port; - switch (pasv_addr.su_family) { - case AF_INET: - a = (char *) &pasv_addr.su_sin.sin_addr; - v4_reply: - 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; - case AF_INET6: - if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { - a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; - goto v4_reply; - } - a = (char *) &pasv_addr.su_sin6.sin6_addr; - 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; - } - } else if (strcmp(cmd, "EPSV") == 0) { - switch (pasv_addr.su_family) { - case AF_INET: - case AF_INET6: - reply(229, "Entering Extended Passive Mode (|||%d|)", - ntohs(pasv_addr.su_port)); - return; - } - } else { - /* more proper error code? */ - } - -pasv_error: - (void) seteuid(pw->pw_uid); - (void) close(pdata); - pdata = -1; - perror_reply(425, "Can't open passive connection"); - return; -} - -/* - * Generate unique name for file with basename "local" - * and open the file in order to avoid possible races. - * Try "local" first, then "local.1", "local.2" etc, up to "local.99". - * Return descriptor to the file, set "name" to its name. - * - * Generates failure reply on error. - */ -static int -guniquefd(char *local, char **name) -{ - static char new[MAXPATHLEN]; - struct stat st; - char *cp; - int count; - int fd; - - cp = strrchr(local, '/'); - if (cp) - *cp = '\0'; - if (stat(cp ? local : ".", &st) < 0) { - perror_reply(553, cp ? local : "."); - return (-1); - } - if (cp) { - /* - * Let not overwrite dirname with counter suffix. - * -4 is for /nn\0 - * In this extreme case dot won't be put in front of suffix. - */ - if (strlen(local) > sizeof(new) - 4) { - reply(553, "Pathname too long."); - return (-1); - } - *cp = '/'; - } - /* -4 is for the .nn<null> we put on the end below */ - (void) snprintf(new, sizeof(new) - 4, "%s", local); - cp = new + strlen(new); - /* - * Don't generate dotfile unless requested explicitly. - * This covers the case when basename gets truncated off - * by buffer size. - */ - if (cp > new && cp[-1] != '/') - *cp++ = '.'; - for (count = 0; count < 100; count++) { - /* At count 0 try unmodified name */ - if (count) - (void)sprintf(cp, "%d", count); - if ((fd = open(count ? new : local, - O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { - *name = count ? new : local; - return (fd); - } - if (errno != EEXIST) { - perror_reply(553, count ? new : local); - return (-1); - } - } - reply(452, "Unique file name cannot be created."); - return (-1); -} - -/* - * Format and send reply containing system error number. - */ -void -perror_reply(int code, char *string) -{ - - reply(code, "%s: %s.", string, strerror(errno)); -} - -static char *onefile[] = { - "", - 0 -}; - -void -send_file_list(char *whichf) -{ - struct stat st; - DIR *dirp = NULL; - struct dirent *dir; - FILE *dout = NULL; - char **dirlist, *dirname; - int simple = 0; - int freeglob = 0; - glob_t gl; - - if (strpbrk(whichf, "~{[*?") != NULL) { - int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; - - memset(&gl, 0, sizeof(gl)); - gl.gl_matchc = MAXGLOBARGS; - flags |= GLOB_LIMIT; - freeglob = 1; - if (glob(whichf, flags, 0, &gl)) { - reply(550, "No matching files found."); - goto out; - } else if (gl.gl_pathc == 0) { - errno = ENOENT; - perror_reply(550, whichf); - goto out; - } - dirlist = gl.gl_pathv; - } else { - onefile[0] = whichf; - dirlist = onefile; - simple = 1; - } - - while ((dirname = *dirlist++)) { - if (stat(dirname, &st) < 0) { - /* - * If user typed "ls -l", etc, and the client - * used NLST, do what the user meant. - */ - if (dirname[0] == '-' && *dirlist == NULL && - dout == NULL) - retrieve(_PATH_LS " %s", dirname); - else - perror_reply(550, whichf); - goto out; - } - - if (S_ISREG(st.st_mode)) { - if (dout == NULL) { - dout = dataconn("file list", -1, "w"); - if (dout == NULL) - goto out; - STARTXFER; - } - START_UNSAFE; - fprintf(dout, "%s%s\n", dirname, - type == TYPE_A ? "\r" : ""); - END_UNSAFE; - if (ferror(dout)) - goto data_err; - byte_count += strlen(dirname) + - (type == TYPE_A ? 2 : 1); - CHECKOOB(goto abrt); - continue; - } else if (!S_ISDIR(st.st_mode)) - continue; - - if ((dirp = opendir(dirname)) == NULL) - continue; - - while ((dir = readdir(dirp)) != NULL) { - char nbuf[MAXPATHLEN]; - - CHECKOOB(goto abrt); - - if (dir->d_name[0] == '.' && dir->d_namlen == 1) - continue; - if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && - dir->d_namlen == 2) - continue; - - snprintf(nbuf, sizeof(nbuf), - "%s/%s", dirname, dir->d_name); - - /* - * We have to do a stat to insure it's - * not a directory or special file. - */ - if (simple || (stat(nbuf, &st) == 0 && - S_ISREG(st.st_mode))) { - if (dout == NULL) { - dout = dataconn("file list", -1, "w"); - if (dout == NULL) - goto out; - STARTXFER; - } - START_UNSAFE; - if (nbuf[0] == '.' && nbuf[1] == '/') - fprintf(dout, "%s%s\n", &nbuf[2], - type == TYPE_A ? "\r" : ""); - else - fprintf(dout, "%s%s\n", nbuf, - type == TYPE_A ? "\r" : ""); - END_UNSAFE; - if (ferror(dout)) - goto data_err; - byte_count += strlen(nbuf) + - (type == TYPE_A ? 2 : 1); - CHECKOOB(goto abrt); - } - } - (void) closedir(dirp); - dirp = NULL; - } - - if (dout == NULL) - reply(550, "No files found."); - else if (ferror(dout)) -data_err: perror_reply(550, "Data connection"); - else - reply(226, "Transfer complete."); -out: - if (dout) { - ENDXFER; -abrt: - (void) fclose(dout); - data = -1; - pdata = -1; - } - if (dirp) - (void) closedir(dirp); - if (freeglob) { - freeglob = 0; - globfree(&gl); - } -} - -void -reapchild(int signo) -{ - while (waitpid(-1, NULL, WNOHANG) > 0); -} - -static void -appendf(char **strp, char *fmt, ...) -{ - va_list ap; - char *ostr, *p; - - va_start(ap, fmt); - vasprintf(&p, fmt, ap); - va_end(ap); - if (p == NULL) - fatalerror("Ran out of memory."); - if (*strp == NULL) - *strp = p; - else { - ostr = *strp; - asprintf(strp, "%s%s", ostr, p); - if (*strp == NULL) - fatalerror("Ran out of memory."); - free(ostr); - } -} - -static void -logcmd(char *cmd, char *file1, char *file2, off_t cnt) -{ - char *msg = NULL; - char wd[MAXPATHLEN + 1]; - - if (logging <= 1) - return; - - if (getcwd(wd, sizeof(wd) - 1) == NULL) - strcpy(wd, strerror(errno)); - - appendf(&msg, "%s", cmd); - if (file1) - appendf(&msg, " %s", file1); - if (file2) - appendf(&msg, " %s", file2); - if (cnt >= 0) - appendf(&msg, " = %jd bytes", (intmax_t)cnt); - appendf(&msg, " (wd: %s", wd); - if (guest || dochroot) - appendf(&msg, "; chrooted"); - appendf(&msg, ")"); - syslog(LOG_INFO, "%s", msg); - free(msg); -} - -static void -logxfer(char *name, off_t size, time_t start) -{ - char buf[MAXPATHLEN + 1024]; - char path[MAXPATHLEN + 1]; - time_t now; - - if (statfd >= 0) { - time(&now); - if (realpath(name, path) == NULL) { - syslog(LOG_NOTICE, "realpath failed on %s: %m", path); - return; - } - snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", - ctime(&now)+4, ident, remotehost, - path, (intmax_t)size, - (long)(now - start + (now == start))); - write(statfd, buf, strlen(buf)); - } -} - -static char * -doublequote(char *s) -{ - int n; - char *p, *s2; - - for (p = s, n = 0; *p; p++) - if (*p == '"') - n++; - - if ((s2 = malloc(p - s + n + 1)) == NULL) - return (NULL); - - for (p = s2; *s; s++, p++) { - if ((*p = *s) == '"') - *(++p) = '"'; - } - *p = '\0'; - - return (s2); -} - -/* setup server socket for specified address family */ -/* if af is PF_UNSPEC more than one socket may be returned */ -/* the returned list is dynamically allocated, so caller needs to free it */ -static int * -socksetup(int af, char *bindname, const char *bindport) -{ - struct addrinfo hints, *res, *r; - int error, maxs, *s, *socks; - const int on = 1; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = af; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(bindname, bindport, &hints, &res); - if (error) { - syslog(LOG_ERR, "%s", gai_strerror(error)); - if (error == EAI_SYSTEM) - syslog(LOG_ERR, "%s", strerror(errno)); - return NULL; - } - - /* Count max number of sockets we may open */ - for (maxs = 0, r = res; r; r = r->ai_next, maxs++) - ; - socks = malloc((maxs + 1) * sizeof(int)); - if (!socks) { - freeaddrinfo(res); - syslog(LOG_ERR, "couldn't allocate memory for sockets"); - return NULL; - } - - *socks = 0; /* num of sockets counter at start of array */ - s = socks + 1; - for (r = res; r; r = r->ai_next) { - *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (*s < 0) { - syslog(LOG_DEBUG, "control socket: %m"); - continue; - } - if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) < 0) - syslog(LOG_WARNING, - "control setsockopt (SO_REUSEADDR): %m"); - if (r->ai_family == AF_INET6) { - if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof(on)) < 0) - syslog(LOG_WARNING, - "control setsockopt (IPV6_V6ONLY): %m"); - } - if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { - syslog(LOG_DEBUG, "control bind: %m"); - close(*s); - continue; - } - (*socks)++; - s++; - } - - if (res) - freeaddrinfo(res); - - if (*socks == 0) { - syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); - free(socks); - return NULL; - } - return(socks); -} diff --git a/libexec/ftpd/ftpusers b/libexec/ftpd/ftpusers deleted file mode 100644 index 2e6f80a5968c..000000000000 --- a/libexec/ftpd/ftpusers +++ /dev/null @@ -1,28 +0,0 @@ -# -# list of users disallowed any ftp access. -# read by ftpd(8). -root -toor -daemon -operator -bin -tty -kmem -games -news -ntpd -man -sshd -smmsp -mailnull -bind -unbound -proxy -_pflogd -_dhcp -uucp -pop -auditdistd -www -hast -nobody diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c deleted file mode 100644 index 85db1f13419d..000000000000 --- a/libexec/ftpd/logwtmp.c +++ /dev/null @@ -1,70 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/socket.h> - -#include <libutil.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <utmpx.h> -#include "extern.h" - -void -ftpd_logwtmp(char *id, char *user, struct sockaddr *addr) -{ - struct utmpx ut; - - memset(&ut, 0, sizeof(ut)); - - if (user != NULL) { - /* Log in. */ - ut.ut_type = USER_PROCESS; - (void)strncpy(ut.ut_user, user, sizeof(ut.ut_user)); - if (addr != NULL) - realhostname_sa(ut.ut_host, sizeof(ut.ut_host), - addr, addr->sa_len); - } else { - /* Log out. */ - ut.ut_type = DEAD_PROCESS; - } - - ut.ut_pid = getpid(); - gettimeofday(&ut.ut_tv, NULL); - (void)strncpy(ut.ut_id, id, sizeof(ut.ut_id)); - (void)strncpy(ut.ut_line, "ftpd", sizeof(ut.ut_line)); - - pututxline(&ut); -} diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h deleted file mode 100644 index 1ff753123b1c..000000000000 --- a/libexec/ftpd/pathnames.h +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -#include <paths.h> - -#define _PATH_FTPCHROOT "/etc/ftpchroot" -#define _PATH_FTPWELCOME "/etc/ftpwelcome" -#define _PATH_FTPLOGINMESG "/etc/ftpmotd" -#define _PATH_FTPHOSTS "/etc/ftphosts" -#define _PATH_FTPDSTATFILE "/var/log/ftpd" -#define _PATH_LS "/bin/ls" diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c deleted file mode 100644 index 5f48356a376f..000000000000 --- a/libexec/ftpd/popen.c +++ /dev/null @@ -1,193 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1988, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software written by Ken Arnold and - * published in UNIX Review, Vol. 6, No. 8. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/types.h> -#include <sys/wait.h> -#include <netinet/in.h> - -#include <errno.h> -#include <glob.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "extern.h" -#include "pathnames.h" -#include <syslog.h> -#include <time.h> - -#define MAXUSRARGS 100 -#define MAXGLOBARGS 1000 - -/* - * 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. - */ -static int *pids; -static int fds; - -FILE * -ftpd_popen(char *program, char *type) -{ - char *cp; - FILE *iop; - int argc, gargc, pdes[2], pid; - char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS]; - - if (((*type != 'r') && (*type != 'w')) || type[1]) - return (NULL); - - if (!pids) { - if ((fds = getdtablesize()) <= 0) - return (NULL); - if ((pids = calloc(fds, sizeof(int))) == NULL) - return (NULL); - } - if (pipe(pdes) < 0) - return (NULL); - - /* break up string into pieces */ - for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) { - if (!(argv[argc++] = strtok(cp, " \t\n"))) - break; - } - argv[argc - 1] = NULL; - - /* glob each piece */ - gargv[0] = argv[0]; - for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) { - glob_t gl; - int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; - - memset(&gl, 0, sizeof(gl)); - gl.gl_matchc = MAXGLOBARGS; - flags |= GLOB_LIMIT; - if (glob(argv[argc], flags, NULL, &gl)) - gargv[gargc++] = strdup(argv[argc]); - else if (gl.gl_pathc > 0) { - for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1); - pop++) - gargv[gargc++] = strdup(*pop); - } - globfree(&gl); - } - gargv[gargc] = NULL; - - iop = NULL; - fflush(NULL); - pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork(); - switch(pid) { - case -1: /* error */ - (void)close(pdes[0]); - (void)close(pdes[1]); - goto pfree; - /* NOTREACHED */ - case 0: /* child */ - if (*type == 'r') { - if (pdes[1] != STDOUT_FILENO) { - dup2(pdes[1], STDOUT_FILENO); - (void)close(pdes[1]); - } - dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ - (void)close(pdes[0]); - } else { - if (pdes[0] != STDIN_FILENO) { - dup2(pdes[0], STDIN_FILENO); - (void)close(pdes[0]); - } - (void)close(pdes[1]); - } - /* Drop privileges before proceeding */ - if (getuid() != geteuid() && setuid(geteuid()) < 0) - _exit(1); - if (strcmp(gargv[0], _PATH_LS) == 0) { - /* Reset getopt for ls_main() */ - optreset = optind = optopt = 1; - /* Close syslogging to remove pwd.db missing msgs */ - closelog(); - /* Trigger to sense new /etc/localtime after chroot */ - if (getenv("TZ") == NULL) { - setenv("TZ", "", 0); - tzset(); - unsetenv("TZ"); - tzset(); - } - exit(ls_main(gargc, gargv)); - } - execv(gargv[0], gargv); - _exit(1); - } - /* parent; assume fdopen can't fail... */ - if (*type == 'r') { - iop = fdopen(pdes[0], type); - (void)close(pdes[1]); - } else { - iop = fdopen(pdes[1], type); - (void)close(pdes[0]); - } - pids[fileno(iop)] = pid; - -pfree: for (argc = 1; gargv[argc] != NULL; argc++) - free(gargv[argc]); - - return (iop); -} - -int -ftpd_pclose(FILE *iop) -{ - int fdes, omask, status; - pid_t pid; - - /* - * pclose returns -1 if stream is not associated with a - * `popened' command, or, if already `pclosed'. - */ - if (pids == NULL || pids[fdes = fileno(iop)] == 0) - return (-1); - (void)fclose(iop); - omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); - while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) - continue; - (void)sigsetmask(omask); - pids[fdes] = 0; - if (pid < 0) - return (pid); - if (WIFEXITED(status)) - return (WEXITSTATUS(status)); - return (1); -} |