155682Smarkm/*
255682Smarkm * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
355682Smarkm *	The Regents of the University of California.  All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm * 3. All advertising materials mentioning features or use of this software
1455682Smarkm *    must display the following acknowledgement:
1555682Smarkm *	This product includes software developed by the University of
1655682Smarkm *	California, Berkeley and its contributors.
1755682Smarkm * 4. Neither the name of the University nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#define	FTP_NAMES
3555682Smarkm#include "ftpd_locl.h"
3655682Smarkm#ifdef KRB5
3755682Smarkm#include <krb5.h>
3855682Smarkm#endif
3955682Smarkm#include "getarg.h"
4055682Smarkm
41233294SstasRCSID("$Id$");
4255682Smarkm
4355682Smarkmstatic char version[] = "Version 6.00";
4455682Smarkm
4555682Smarkmextern	off_t restart_point;
4655682Smarkmextern	char cbuf[];
4755682Smarkm
4855682Smarkmstruct  sockaddr_storage ctrl_addr_ss;
4955682Smarkmstruct  sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss;
5055682Smarkm
5155682Smarkmstruct  sockaddr_storage data_source_ss;
5255682Smarkmstruct  sockaddr *data_source = (struct sockaddr *)&data_source_ss;
5355682Smarkm
5455682Smarkmstruct  sockaddr_storage data_dest_ss;
5555682Smarkmstruct  sockaddr *data_dest = (struct sockaddr *)&data_dest_ss;
5655682Smarkm
5755682Smarkmstruct  sockaddr_storage his_addr_ss;
5855682Smarkmstruct  sockaddr *his_addr = (struct sockaddr *)&his_addr_ss;
5955682Smarkm
6055682Smarkmstruct  sockaddr_storage pasv_addr_ss;
6155682Smarkmstruct  sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss;
6255682Smarkm
6355682Smarkmint	data;
6455682Smarkmint	logged_in;
6555682Smarkmstruct	passwd *pw;
6655682Smarkmint	debug = 0;
6755682Smarkmint	ftpd_timeout = 900;    /* timeout after 15 minutes of inactivity */
6855682Smarkmint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
6990926Snectarint	restricted_data_ports = 1;
7055682Smarkmint	logging;
7155682Smarkmint	guest;
7255682Smarkmint	dochroot;
7355682Smarkmint	type;
7455682Smarkmint	form;
7555682Smarkmint	stru;			/* avoid C keyword */
7655682Smarkmint	mode;
7755682Smarkmint	usedefault = 1;		/* for data transfers */
7855682Smarkmint	pdata = -1;		/* for passive mode */
79142403Snectarint	allow_insecure_oob = 1;
80142403Snectarstatic int transflag;
81142403Snectarstatic int urgflag;
8255682Smarkmoff_t	file_size;
8355682Smarkmoff_t	byte_count;
8455682Smarkm#if !defined(CMASK) || CMASK == 0
8555682Smarkm#undef CMASK
8655682Smarkm#define CMASK 027
8755682Smarkm#endif
8855682Smarkmint	defumask = CMASK;		/* default umask value */
8955682Smarkmint	guest_umask = 0777;	/* Paranoia for anonymous users */
9055682Smarkmchar	tmpline[10240];
9155682Smarkmchar	hostname[MaxHostNameLen];
9255682Smarkmchar	remotehost[MaxHostNameLen];
9355682Smarkmstatic char ttyline[20];
94233294Sstasint     paranoid = 1;
9555682Smarkm
9655682Smarkm#define AUTH_PLAIN	(1 << 0) /* allow sending passwords */
9755682Smarkm#define AUTH_OTP	(1 << 1) /* passwords are one-time */
9855682Smarkm#define AUTH_FTP	(1 << 2) /* allow anonymous login */
9955682Smarkm
10055682Smarkmstatic int auth_level = 0; /* Only allow kerberos login by default */
10155682Smarkm
10255682Smarkm/*
10355682Smarkm * Timeout intervals for retrying connections
10455682Smarkm * to hosts that don't accept PORT cmds.  This
10555682Smarkm * is a kludge, but given the problems with TCP...
10655682Smarkm */
10755682Smarkm#define	SWAITMAX	90	/* wait at most 90 seconds */
10855682Smarkm#define	SWAITINT	5	/* interval between retries */
10955682Smarkm
11055682Smarkmint	swaitmax = SWAITMAX;
11155682Smarkmint	swaitint = SWAITINT;
11255682Smarkm
11355682Smarkm#ifdef HAVE_SETPROCTITLE
11455682Smarkmchar	proctitle[BUFSIZ];	/* initial part of title */
11555682Smarkm#endif /* HAVE_SETPROCTITLE */
11655682Smarkm
11755682Smarkm#define LOGCMD(cmd, file) \
11855682Smarkm	if (logging > 1) \
11955682Smarkm	    syslog(LOG_INFO,"%s %s%s", cmd, \
12055682Smarkm		*(file) == '/' ? "" : curdir(), file);
12155682Smarkm#define LOGCMD2(cmd, file1, file2) \
12255682Smarkm	 if (logging > 1) \
12355682Smarkm	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
12455682Smarkm		*(file1) == '/' ? "" : curdir(), file1, \
12555682Smarkm		*(file2) == '/' ? "" : curdir(), file2);
12655682Smarkm#define LOGBYTES(cmd, file, cnt) \
12755682Smarkm	if (logging > 1) { \
12855682Smarkm		if (cnt == (off_t)-1) \
12955682Smarkm		    syslog(LOG_INFO,"%s %s%s", cmd, \
13055682Smarkm			*(file) == '/' ? "" : curdir(), file); \
13155682Smarkm		else \
13255682Smarkm		    syslog(LOG_INFO, "%s %s%s = %ld bytes", \
13355682Smarkm			cmd, (*(file) == '/') ? "" : curdir(), file, (long)cnt); \
13455682Smarkm	}
13555682Smarkm
13655682Smarkmstatic void	 ack (char *);
13755682Smarkmstatic void	 myoob (int);
138142403Snectarstatic int	 handleoobcmd(void);
13955682Smarkmstatic int	 checkuser (char *, char *);
14055682Smarkmstatic int	 checkaccess (char *);
14155682Smarkmstatic FILE	*dataconn (const char *, off_t, const char *);
142178825Sdfrstatic void	 dolog (struct sockaddr *, int);
14355682Smarkmstatic void	 end_login (void);
144178825Sdfrstatic FILE	*getdatasock (const char *, int);
14555682Smarkmstatic char	*gunique (char *);
14655682Smarkmstatic RETSIGTYPE	 lostconn (int);
14755682Smarkmstatic int	 receive_data (FILE *, FILE *);
14855682Smarkmstatic void	 send_data (FILE *, FILE *);
14955682Smarkmstatic struct passwd * sgetpwnam (char *);
15055682Smarkm
15155682Smarkmstatic char *
15255682Smarkmcurdir(void)
15355682Smarkm{
15455682Smarkm	static char path[MaxPathLen+1];	/* path + '/' + '\0' */
15555682Smarkm
15655682Smarkm	if (getcwd(path, sizeof(path)-1) == NULL)
15755682Smarkm		return ("");
15855682Smarkm	if (path[1] != '\0')		/* special case for root dir. */
15955682Smarkm		strlcat(path, "/", sizeof(path));
16055682Smarkm	/* For guest account, skip / since it's chrooted */
16155682Smarkm	return (guest ? path+1 : path);
16255682Smarkm}
16355682Smarkm
16455682Smarkm#ifndef LINE_MAX
16555682Smarkm#define LINE_MAX 1024
16655682Smarkm#endif
16755682Smarkm
16855682Smarkmstatic int
16955682Smarkmparse_auth_level(char *str)
17055682Smarkm{
17155682Smarkm    char *p;
17255682Smarkm    int ret = 0;
17355682Smarkm    char *foo = NULL;
17455682Smarkm
17555682Smarkm    for(p = strtok_r(str, ",", &foo);
17655682Smarkm	p;
17755682Smarkm	p = strtok_r(NULL, ",", &foo)) {
17855682Smarkm	if(strcmp(p, "user") == 0)
17955682Smarkm	    ;
18055682Smarkm#ifdef OTP
18155682Smarkm	else if(strcmp(p, "otp") == 0)
18255682Smarkm	    ret |= AUTH_PLAIN|AUTH_OTP;
18355682Smarkm#endif
18455682Smarkm	else if(strcmp(p, "ftp") == 0 ||
18555682Smarkm		strcmp(p, "safe") == 0)
18655682Smarkm	    ret |= AUTH_FTP;
18755682Smarkm	else if(strcmp(p, "plain") == 0)
18855682Smarkm	    ret |= AUTH_PLAIN;
18955682Smarkm	else if(strcmp(p, "none") == 0)
19055682Smarkm	    ret |= AUTH_PLAIN|AUTH_FTP;
19155682Smarkm	else
19255682Smarkm	    warnx("bad value for -a: `%s'", p);
19355682Smarkm    }
194233294Sstas    return ret;
19555682Smarkm}
19655682Smarkm
19755682Smarkm/*
19855682Smarkm * Print usage and die.
19955682Smarkm */
20055682Smarkm
20155682Smarkmstatic int interactive_flag;
20255682Smarkmstatic char *guest_umask_string;
20355682Smarkmstatic char *port_string;
20455682Smarkmstatic char *umask_string;
20555682Smarkmstatic char *auth_string;
20655682Smarkm
20755682Smarkmint use_builtin_ls = -1;
20855682Smarkm
20955682Smarkmstatic int help_flag;
21055682Smarkmstatic int version_flag;
21155682Smarkm
21272445Sassarstatic const char *good_chars = "+-=_,.";
21372445Sassar
21455682Smarkmstruct getargs args[] = {
21555682Smarkm    { NULL, 'a', arg_string, &auth_string, "required authentication" },
21655682Smarkm    { NULL, 'i', arg_flag, &interactive_flag, "don't assume stdin is a socket" },
21755682Smarkm    { NULL, 'p', arg_string, &port_string, "what port to listen to" },
21855682Smarkm    { NULL, 'g', arg_string, &guest_umask_string, "umask for guest logins" },
21955682Smarkm    { NULL, 'l', arg_counter, &logging, "log more stuff", "" },
22055682Smarkm    { NULL, 't', arg_integer, &ftpd_timeout, "initial timeout" },
22155682Smarkm    { NULL, 'T', arg_integer, &maxtimeout, "max timeout" },
22255682Smarkm    { NULL, 'u', arg_string, &umask_string, "umask for user logins" },
22390926Snectar    { NULL, 'U', arg_negative_flag, &restricted_data_ports, "don't use high data ports" },
22472445Sassar    { NULL, 'd', arg_flag, &debug, "enable debugging" },
22572445Sassar    { NULL, 'v', arg_flag, &debug, "enable debugging" },
22655682Smarkm    { "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" },
22772445Sassar    { "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" },
228142403Snectar    { "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" },
229233294Sstas#ifdef KRB5
230127808Snectar    { "gss-bindings", 0,  arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL},
231127808Snectar#endif
23255682Smarkm    { "version", 0, arg_flag, &version_flag },
23355682Smarkm    { "help", 'h', arg_flag, &help_flag }
23455682Smarkm};
23555682Smarkm
23655682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]);
23755682Smarkm
23855682Smarkmstatic void
23955682Smarkmusage (int code)
24055682Smarkm{
24155682Smarkm    arg_printusage(args, num_args, NULL, "");
24255682Smarkm    exit (code);
24355682Smarkm}
24455682Smarkm
24555682Smarkm/* output contents of a file */
24655682Smarkmstatic int
24755682Smarkmshow_file(const char *file, int code)
24855682Smarkm{
24955682Smarkm    FILE *f;
25055682Smarkm    char buf[128];
25155682Smarkm
25255682Smarkm    f = fopen(file, "r");
25355682Smarkm    if(f == NULL)
25455682Smarkm	return -1;
25555682Smarkm    while(fgets(buf, sizeof(buf), f)){
25655682Smarkm	buf[strcspn(buf, "\r\n")] = '\0';
25755682Smarkm	lreply(code, "%s", buf);
25855682Smarkm    }
25955682Smarkm    fclose(f);
26055682Smarkm    return 0;
26155682Smarkm}
26255682Smarkm
26355682Smarkmint
26455682Smarkmmain(int argc, char **argv)
26555682Smarkm{
26672445Sassar    socklen_t his_addr_len, ctrl_addr_len;
26772445Sassar    int on = 1;
26855682Smarkm    int port;
26955682Smarkm    struct servent *sp;
27055682Smarkm
27155682Smarkm    int optind = 0;
27255682Smarkm
27378527Sassar    setprogname (argv[0]);
27455682Smarkm
27555682Smarkm    if(getarg(args, num_args, argc, argv, &optind))
27655682Smarkm	usage(1);
27755682Smarkm
27855682Smarkm    if(help_flag)
27955682Smarkm	usage(0);
280233294Sstas
28155682Smarkm    if(version_flag) {
28255682Smarkm	print_version(NULL);
28355682Smarkm	exit(0);
28455682Smarkm    }
28555682Smarkm
28655682Smarkm    if(auth_string)
28755682Smarkm	auth_level = parse_auth_level(auth_string);
28855682Smarkm    {
28955682Smarkm	char *p;
29055682Smarkm	long val = 0;
291233294Sstas
29255682Smarkm	if(guest_umask_string) {
29355682Smarkm	    val = strtol(guest_umask_string, &p, 8);
29455682Smarkm	    if (*p != '\0' || val < 0)
29555682Smarkm		warnx("bad value for -g");
29655682Smarkm	    else
29755682Smarkm		guest_umask = val;
29855682Smarkm	}
29955682Smarkm	if(umask_string) {
30055682Smarkm	    val = strtol(umask_string, &p, 8);
30155682Smarkm	    if (*p != '\0' || val < 0)
30255682Smarkm		warnx("bad value for -u");
30355682Smarkm	    else
30455682Smarkm		defumask = val;
30555682Smarkm	}
30655682Smarkm    }
307102644Snectar    sp = getservbyname("ftp", "tcp");
308102644Snectar    if(sp)
309102644Snectar	port = sp->s_port;
310102644Snectar    else
311102644Snectar	port = htons(21);
31255682Smarkm    if(port_string) {
31355682Smarkm	sp = getservbyname(port_string, "tcp");
31455682Smarkm	if(sp)
31555682Smarkm	    port = sp->s_port;
31655682Smarkm	else
317120945Snectar	    if(isdigit((unsigned char)port_string[0]))
31855682Smarkm		port = htons(atoi(port_string));
31955682Smarkm	    else
32055682Smarkm		warnx("bad value for -p");
32155682Smarkm    }
322233294Sstas
32355682Smarkm    if (maxtimeout < ftpd_timeout)
32455682Smarkm	maxtimeout = ftpd_timeout;
32555682Smarkm
32655682Smarkm#if 0
32755682Smarkm    if (ftpd_timeout > maxtimeout)
32855682Smarkm	ftpd_timeout = maxtimeout;
32955682Smarkm#endif
33055682Smarkm
33155682Smarkm    if(interactive_flag)
332233294Sstas	mini_inetd(port, NULL);
33355682Smarkm
33455682Smarkm    /*
33555682Smarkm     * LOG_NDELAY sets up the logging connection immediately,
33655682Smarkm     * necessary for anonymous ftp's that chroot and can't do it later.
33755682Smarkm     */
33855682Smarkm    openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
33955682Smarkm    his_addr_len = sizeof(his_addr_ss);
34055682Smarkm    if (getpeername(STDIN_FILENO, his_addr, &his_addr_len) < 0) {
34155682Smarkm	syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
34255682Smarkm	exit(1);
34355682Smarkm    }
34455682Smarkm    ctrl_addr_len = sizeof(ctrl_addr_ss);
34555682Smarkm    if (getsockname(STDIN_FILENO, ctrl_addr, &ctrl_addr_len) < 0) {
34655682Smarkm	syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
34755682Smarkm	exit(1);
34855682Smarkm    }
349233294Sstas#if defined(IP_TOS)
350233294Sstas    if (ctrl_addr->sa_family == AF_INET)
351233294Sstas	socket_set_tos(STDIN_FILENO, IP_TOS);
35255682Smarkm#endif
35355682Smarkm    data_source->sa_family = ctrl_addr->sa_family;
35455682Smarkm    socket_set_port (data_source,
35555682Smarkm		     htons(ntohs(socket_get_port(ctrl_addr)) - 1));
35655682Smarkm
35755682Smarkm    /* set this here so it can be put in wtmp */
35855682Smarkm    snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid());
35955682Smarkm
36055682Smarkm
36155682Smarkm    /*	freopen(_PATH_DEVNULL, "w", stderr); */
36255682Smarkm    signal(SIGPIPE, lostconn);
36355682Smarkm    signal(SIGCHLD, SIG_IGN);
36455682Smarkm#ifdef SIGURG
36555682Smarkm    if (signal(SIGURG, myoob) == SIG_ERR)
36655682Smarkm	syslog(LOG_ERR, "signal: %m");
36755682Smarkm#endif
36855682Smarkm
36955682Smarkm    /* Try to handle urgent data inline */
37055682Smarkm#if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT)
37155682Smarkm    if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (void *)&on,
37255682Smarkm		   sizeof(on)) < 0)
37355682Smarkm	syslog(LOG_ERR, "setsockopt: %m");
37455682Smarkm#endif
37555682Smarkm
37655682Smarkm#ifdef	F_SETOWN
37755682Smarkm    if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
37855682Smarkm	syslog(LOG_ERR, "fcntl F_SETOWN: %m");
37955682Smarkm#endif
38055682Smarkm    dolog(his_addr, his_addr_len);
38155682Smarkm    /*
38255682Smarkm     * Set up default state
38355682Smarkm     */
38455682Smarkm    data = -1;
38555682Smarkm    type = TYPE_A;
38655682Smarkm    form = FORM_N;
38755682Smarkm    stru = STRU_F;
38855682Smarkm    mode = MODE_S;
38955682Smarkm    tmpline[0] = '\0';
39055682Smarkm
39155682Smarkm    /* If logins are disabled, print out the message. */
39255682Smarkm    if(show_file(_PATH_NOLOGIN, 530) == 0) {
39355682Smarkm	reply(530, "System not available.");
39455682Smarkm	exit(0);
39555682Smarkm    }
39655682Smarkm    show_file(_PATH_FTPWELCOME, 220);
39755682Smarkm    /* reply(220,) must follow */
39855682Smarkm    gethostname(hostname, sizeof(hostname));
399233294Sstas
40055682Smarkm    reply(220, "%s FTP server (%s"
40155682Smarkm#ifdef KRB5
40255682Smarkm	  "+%s"
40355682Smarkm#endif
40455682Smarkm	  ") ready.", hostname, version
40555682Smarkm#ifdef KRB5
40655682Smarkm	  ,heimdal_version
40755682Smarkm#endif
40855682Smarkm	  );
40955682Smarkm
41055682Smarkm    for (;;)
41155682Smarkm	yyparse();
41255682Smarkm    /* NOTREACHED */
41355682Smarkm}
41455682Smarkm
41555682Smarkmstatic RETSIGTYPE
41655682Smarkmlostconn(int signo)
41755682Smarkm{
41855682Smarkm
41955682Smarkm	if (debug)
42055682Smarkm		syslog(LOG_DEBUG, "lost connection");
42155682Smarkm	dologout(-1);
42255682Smarkm}
42355682Smarkm
42455682Smarkm/*
42555682Smarkm * Helper function for sgetpwnam().
42655682Smarkm */
42755682Smarkmstatic char *
42855682Smarkmsgetsave(char *s)
42955682Smarkm{
43055682Smarkm	char *new = strdup(s);
43155682Smarkm
43255682Smarkm	if (new == NULL) {
43355682Smarkm		perror_reply(421, "Local resource failure: malloc");
43455682Smarkm		dologout(1);
43555682Smarkm		/* NOTREACHED */
43655682Smarkm	}
43755682Smarkm	return new;
43855682Smarkm}
43955682Smarkm
44055682Smarkm/*
44155682Smarkm * Save the result of a getpwnam.  Used for USER command, since
44255682Smarkm * the data returned must not be clobbered by any other command
44355682Smarkm * (e.g., globbing).
44455682Smarkm */
44555682Smarkmstatic struct passwd *
44655682Smarkmsgetpwnam(char *name)
44755682Smarkm{
44855682Smarkm	static struct passwd save;
44955682Smarkm	struct passwd *p;
45055682Smarkm
45155682Smarkm	if ((p = k_getpwnam(name)) == NULL)
45255682Smarkm		return (p);
45355682Smarkm	if (save.pw_name) {
45455682Smarkm		free(save.pw_name);
45555682Smarkm		free(save.pw_passwd);
45655682Smarkm		free(save.pw_gecos);
45755682Smarkm		free(save.pw_dir);
45855682Smarkm		free(save.pw_shell);
45955682Smarkm	}
46055682Smarkm	save = *p;
46155682Smarkm	save.pw_name = sgetsave(p->pw_name);
46255682Smarkm	save.pw_passwd = sgetsave(p->pw_passwd);
46355682Smarkm	save.pw_gecos = sgetsave(p->pw_gecos);
46455682Smarkm	save.pw_dir = sgetsave(p->pw_dir);
46555682Smarkm	save.pw_shell = sgetsave(p->pw_shell);
46655682Smarkm	return (&save);
46755682Smarkm}
46855682Smarkm
46955682Smarkmstatic int login_attempts;	/* number of failed login attempts */
47055682Smarkmstatic int askpasswd;		/* had user command, ask for passwd */
47155682Smarkmstatic char curname[10];	/* current USER name */
47255682Smarkm#ifdef OTP
47355682SmarkmOtpContext otp_ctx;
47455682Smarkm#endif
47555682Smarkm
47655682Smarkm/*
47755682Smarkm * USER command.
47855682Smarkm * Sets global passwd pointer pw if named account exists and is acceptable;
47955682Smarkm * sets askpasswd if a PASS command is expected.  If logged in previously,
48055682Smarkm * need to reset state.  If name is "ftp" or "anonymous", the name is not in
48155682Smarkm * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
48255682Smarkm * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
48355682Smarkm * requesting login privileges.  Disallow anyone who does not have a standard
48455682Smarkm * shell as returned by getusershell().  Disallow anyone mentioned in the file
48555682Smarkm * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
48655682Smarkm */
48755682Smarkmvoid
48855682Smarkmuser(char *name)
48955682Smarkm{
49055682Smarkm	char *cp, *shell;
49155682Smarkm
49255682Smarkm	if(auth_level == 0 && !sec_complete){
49355682Smarkm	    reply(530, "No login allowed without authorization.");
49455682Smarkm	    return;
49555682Smarkm	}
49655682Smarkm
49755682Smarkm	if (logged_in) {
49855682Smarkm		if (guest) {
49955682Smarkm			reply(530, "Can't change user from guest login.");
50055682Smarkm			return;
50155682Smarkm		} else if (dochroot) {
50255682Smarkm			reply(530, "Can't change user from chroot user.");
50355682Smarkm			return;
50455682Smarkm		}
50555682Smarkm		end_login();
50655682Smarkm	}
50755682Smarkm
50855682Smarkm	guest = 0;
50955682Smarkm	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
51055682Smarkm	    if ((auth_level & AUTH_FTP) == 0 ||
511233294Sstas		checkaccess("ftp") ||
51255682Smarkm		checkaccess("anonymous"))
51355682Smarkm		reply(530, "User %s access denied.", name);
51455682Smarkm	    else if ((pw = sgetpwnam("ftp")) != NULL) {
51555682Smarkm		guest = 1;
51655682Smarkm		defumask = guest_umask;	/* paranoia for incoming */
51755682Smarkm		askpasswd = 1;
51855682Smarkm		reply(331, "Guest login ok, type your name as password.");
51955682Smarkm	    } else
52055682Smarkm		reply(530, "User %s unknown.", name);
52155682Smarkm	    if (!askpasswd && logging) {
52255682Smarkm		char data_addr[256];
52355682Smarkm
52455682Smarkm		if (inet_ntop (his_addr->sa_family,
52555682Smarkm			       socket_get_address(his_addr),
52655682Smarkm			       data_addr, sizeof(data_addr)) == NULL)
52755682Smarkm			strlcpy (data_addr, "unknown address",
52855682Smarkm					 sizeof(data_addr));
52955682Smarkm
53055682Smarkm		syslog(LOG_NOTICE,
53155682Smarkm		       "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)",
53255682Smarkm		       remotehost, data_addr);
53355682Smarkm	    }
53455682Smarkm	    return;
53555682Smarkm	}
53655682Smarkm	if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){
53755682Smarkm	    reply(530, "Only authorized and anonymous login allowed.");
53855682Smarkm	    return;
53955682Smarkm	}
54055682Smarkm	if ((pw = sgetpwnam(name))) {
54155682Smarkm		if ((shell = pw->pw_shell) == NULL || *shell == 0)
54255682Smarkm			shell = _PATH_BSHELL;
54355682Smarkm		while ((cp = getusershell()) != NULL)
54455682Smarkm			if (strcmp(cp, shell) == 0)
54555682Smarkm				break;
54655682Smarkm		endusershell();
54755682Smarkm
54855682Smarkm		if (cp == NULL || checkaccess(name)) {
54955682Smarkm			reply(530, "User %s access denied.", name);
55055682Smarkm			if (logging) {
55155682Smarkm				char data_addr[256];
55255682Smarkm
55355682Smarkm				if (inet_ntop (his_addr->sa_family,
55455682Smarkm					       socket_get_address(his_addr),
55555682Smarkm					       data_addr,
55655682Smarkm					       sizeof(data_addr)) == NULL)
55755682Smarkm					strlcpy (data_addr,
55855682Smarkm							 "unknown address",
55955682Smarkm							 sizeof(data_addr));
56055682Smarkm
56155682Smarkm				syslog(LOG_NOTICE,
56255682Smarkm				       "FTP LOGIN REFUSED FROM %s(%s), %s",
56355682Smarkm				       remotehost,
56455682Smarkm				       data_addr,
56555682Smarkm				       name);
56655682Smarkm			}
56755682Smarkm			pw = (struct passwd *) NULL;
56855682Smarkm			return;
56955682Smarkm		}
57055682Smarkm	}
57155682Smarkm	if (logging)
57255682Smarkm	    strlcpy(curname, name, sizeof(curname));
57355682Smarkm	if(sec_complete) {
574178825Sdfr	    if(sec_userok(name) == 0) {
57555682Smarkm		do_login(232, name);
576178825Sdfr		sec_session(name);
577178825Sdfr	    } else
57855682Smarkm		reply(530, "User %s access denied.", name);
57955682Smarkm	} else {
580178825Sdfr#ifdef OTP
58155682Smarkm		char ss[256];
58255682Smarkm
58355682Smarkm		if (otp_challenge(&otp_ctx, name, ss, sizeof(ss)) == 0) {
58455682Smarkm			reply(331, "Password %s for %s required.",
58555682Smarkm			      ss, name);
58655682Smarkm			askpasswd = 1;
58755682Smarkm		} else
58855682Smarkm#endif
58955682Smarkm		if ((auth_level & AUTH_OTP) == 0) {
59055682Smarkm		    reply(331, "Password required for %s.", name);
59155682Smarkm		    askpasswd = 1;
59255682Smarkm		} else {
593178825Sdfr#ifdef OTP
59455682Smarkm		    char *s;
595178825Sdfr
59655682Smarkm		    if ((s = otp_error (&otp_ctx)) != NULL)
59755682Smarkm			lreply(530, "OTP: %s", s);
59855682Smarkm#endif
59955682Smarkm		    reply(530,
60055682Smarkm			  "Only authorized, anonymous"
60155682Smarkm#ifdef OTP
60255682Smarkm			  " and OTP "
60355682Smarkm#endif
60455682Smarkm			  "login allowed.");
60555682Smarkm		}
60655682Smarkm
60755682Smarkm	}
60855682Smarkm	/*
60955682Smarkm	 * Delay before reading passwd after first failed
61055682Smarkm	 * attempt to slow down passwd-guessing programs.
61155682Smarkm	 */
61255682Smarkm	if (login_attempts)
61355682Smarkm		sleep(login_attempts);
61455682Smarkm}
61555682Smarkm
61655682Smarkm/*
61755682Smarkm * Check if a user is in the file "fname"
61855682Smarkm */
61955682Smarkmstatic int
62055682Smarkmcheckuser(char *fname, char *name)
62155682Smarkm{
62255682Smarkm	FILE *fd;
62355682Smarkm	int found = 0;
62455682Smarkm	char *p, line[BUFSIZ];
62555682Smarkm
62655682Smarkm	if ((fd = fopen(fname, "r")) != NULL) {
62755682Smarkm		while (fgets(line, sizeof(line), fd) != NULL)
62855682Smarkm			if ((p = strchr(line, '\n')) != NULL) {
62955682Smarkm				*p = '\0';
63055682Smarkm				if (line[0] == '#')
63155682Smarkm					continue;
63255682Smarkm				if (strcmp(line, name) == 0) {
63355682Smarkm					found = 1;
63455682Smarkm					break;
63555682Smarkm				}
63655682Smarkm			}
63755682Smarkm		fclose(fd);
63855682Smarkm	}
63955682Smarkm	return (found);
64055682Smarkm}
64155682Smarkm
64255682Smarkm
64355682Smarkm/*
644233294Sstas * Determine whether a user has access, based on information in
64555682Smarkm * _PATH_FTPUSERS. The users are listed one per line, with `allow'
64655682Smarkm * or `deny' after the username. If anything other than `allow', or
64755682Smarkm * just nothing, is given after the username, `deny' is assumed.
64855682Smarkm *
64955682Smarkm * If the user is not found in the file, but the pseudo-user `*' is,
65055682Smarkm * the permission is taken from that line.
65155682Smarkm *
65255682Smarkm * This preserves the old semantics where if a user was listed in the
65355682Smarkm * file he was denied, otherwise he was allowed.
65455682Smarkm *
65555682Smarkm * Return 1 if the user is denied, or 0 if he is allowed.  */
65655682Smarkm
65755682Smarkmstatic int
65855682Smarkmmatch(const char *pattern, const char *string)
65955682Smarkm{
66055682Smarkm    return fnmatch(pattern, string, FNM_NOESCAPE);
66155682Smarkm}
66255682Smarkm
66355682Smarkmstatic int
66455682Smarkmcheckaccess(char *name)
66555682Smarkm{
66655682Smarkm#define ALLOWED		0
66755682Smarkm#define	NOT_ALLOWED	1
66855682Smarkm    FILE *fd;
66955682Smarkm    int allowed = ALLOWED;
67055682Smarkm    char *user, *perm, line[BUFSIZ];
67155682Smarkm    char *foo;
672233294Sstas
67355682Smarkm    fd = fopen(_PATH_FTPUSERS, "r");
674233294Sstas
67555682Smarkm    if(fd == NULL)
67655682Smarkm	return allowed;
67755682Smarkm
67855682Smarkm    while (fgets(line, sizeof(line), fd) != NULL)  {
67955682Smarkm	foo = NULL;
68055682Smarkm	user = strtok_r(line, " \t\n", &foo);
68155682Smarkm	if (user == NULL || user[0] == '#')
68255682Smarkm	    continue;
68355682Smarkm	perm = strtok_r(NULL, " \t\n", &foo);
68455682Smarkm	if (match(user, name) == 0){
68555682Smarkm	    if(perm && strcmp(perm, "allow") == 0)
68655682Smarkm		allowed = ALLOWED;
68755682Smarkm	    else
68855682Smarkm		allowed = NOT_ALLOWED;
68955682Smarkm	    break;
69055682Smarkm	}
69155682Smarkm    }
69255682Smarkm    fclose(fd);
69355682Smarkm    return allowed;
69455682Smarkm}
69555682Smarkm#undef	ALLOWED
69655682Smarkm#undef	NOT_ALLOWED
69755682Smarkm
69855682Smarkm
69955682Smarkmint do_login(int code, char *passwd)
70055682Smarkm{
70155682Smarkm    login_attempts = 0;		/* this time successful */
70255682Smarkm    if (setegid((gid_t)pw->pw_gid) < 0) {
70355682Smarkm	reply(550, "Can't set gid.");
70455682Smarkm	return -1;
70555682Smarkm    }
70655682Smarkm    initgroups(pw->pw_name, pw->pw_gid);
707233294Sstas#if defined(KRB5)
708178825Sdfr    if(k_hasafs())
709178825Sdfr	k_setpag();
710178825Sdfr#endif
71155682Smarkm
71255682Smarkm    /* open wtmp before chroot */
71355682Smarkm    ftpd_logwtmp(ttyline, pw->pw_name, remotehost);
71455682Smarkm    logged_in = 1;
71555682Smarkm
71655682Smarkm    dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
71755682Smarkm    if (guest) {
71855682Smarkm	/*
71955682Smarkm	 * We MUST do a chdir() after the chroot. Otherwise
72055682Smarkm	 * the old current directory will be accessible as "."
72155682Smarkm	 * outside the new root!
72255682Smarkm	 */
72355682Smarkm	if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
72455682Smarkm	    reply(550, "Can't set guest privileges.");
72555682Smarkm	    return -1;
72655682Smarkm	}
72755682Smarkm    } else if (dochroot) {
72855682Smarkm	if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
72955682Smarkm	    reply(550, "Can't change root.");
73055682Smarkm	    return -1;
73155682Smarkm	}
73255682Smarkm    } else if (chdir(pw->pw_dir) < 0) {
73355682Smarkm	if (chdir("/") < 0) {
73455682Smarkm	    reply(530, "User %s: can't change directory to %s.",
73555682Smarkm		  pw->pw_name, pw->pw_dir);
73655682Smarkm	    return -1;
73755682Smarkm	} else
73855682Smarkm	    lreply(code, "No directory! Logging in with home=/");
73955682Smarkm    }
74055682Smarkm    if (seteuid((uid_t)pw->pw_uid) < 0) {
74155682Smarkm	reply(550, "Can't set uid.");
74255682Smarkm	return -1;
74355682Smarkm    }
74455682Smarkm
74555682Smarkm    if(use_builtin_ls == -1) {
74655682Smarkm	struct stat st;
74755682Smarkm	/* if /bin/ls exist and is a regular file, use it, otherwise
74855682Smarkm           use built-in ls */
74955682Smarkm	if(stat("/bin/ls", &st) == 0 &&
75055682Smarkm	   S_ISREG(st.st_mode))
75155682Smarkm	    use_builtin_ls = 0;
75255682Smarkm	else
75355682Smarkm	    use_builtin_ls = 1;
75455682Smarkm    }
75555682Smarkm
75655682Smarkm    /*
75755682Smarkm     * Display a login message, if it exists.
75855682Smarkm     * N.B. reply(code,) must follow the message.
75955682Smarkm     */
76055682Smarkm    show_file(_PATH_FTPLOGINMESG, code);
76155682Smarkm    if(show_file(_PATH_ISSUE_NET, code) != 0)
76255682Smarkm	show_file(_PATH_ISSUE, code);
76355682Smarkm    if (guest) {
76455682Smarkm	reply(code, "Guest login ok, access restrictions apply.");
76555682Smarkm#ifdef HAVE_SETPROCTITLE
76655682Smarkm	snprintf (proctitle, sizeof(proctitle),
76755682Smarkm		  "%s: anonymous/%s",
76855682Smarkm		  remotehost,
76955682Smarkm		  passwd);
77064593Skris	setproctitle("%s", proctitle);
77155682Smarkm#endif /* HAVE_SETPROCTITLE */
77255682Smarkm	if (logging) {
77355682Smarkm	    char data_addr[256];
77455682Smarkm
77555682Smarkm	    if (inet_ntop (his_addr->sa_family,
77655682Smarkm			   socket_get_address(his_addr),
77755682Smarkm			   data_addr, sizeof(data_addr)) == NULL)
77855682Smarkm		strlcpy (data_addr, "unknown address",
77955682Smarkm				 sizeof(data_addr));
78055682Smarkm
78155682Smarkm	    syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s",
782233294Sstas		   remotehost,
78355682Smarkm		   data_addr,
78455682Smarkm		   passwd);
78555682Smarkm	}
78655682Smarkm    } else {
78755682Smarkm	reply(code, "User %s logged in.", pw->pw_name);
78855682Smarkm#ifdef HAVE_SETPROCTITLE
78955682Smarkm	snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
79064593Skris	setproctitle("%s", proctitle);
79155682Smarkm#endif /* HAVE_SETPROCTITLE */
79255682Smarkm	if (logging) {
79355682Smarkm	    char data_addr[256];
79455682Smarkm
79555682Smarkm	    if (inet_ntop (his_addr->sa_family,
79655682Smarkm			   socket_get_address(his_addr),
79755682Smarkm			   data_addr, sizeof(data_addr)) == NULL)
79855682Smarkm		strlcpy (data_addr, "unknown address",
79955682Smarkm				 sizeof(data_addr));
80055682Smarkm
80155682Smarkm	    syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s",
80255682Smarkm		   remotehost,
80355682Smarkm		   data_addr,
80455682Smarkm		   pw->pw_name);
80555682Smarkm	}
80655682Smarkm    }
80755682Smarkm    umask(defumask);
80855682Smarkm    return 0;
80955682Smarkm}
81055682Smarkm
81155682Smarkm/*
81255682Smarkm * Terminate login as previous user, if any, resetting state;
81355682Smarkm * used when USER command is given or login fails.
81455682Smarkm */
81555682Smarkmstatic void
81655682Smarkmend_login(void)
81755682Smarkm{
81855682Smarkm
819178825Sdfr	if (seteuid((uid_t)0) < 0)
820178825Sdfr		fatal("Failed to seteuid");
82155682Smarkm	if (logged_in)
82255682Smarkm		ftpd_logwtmp(ttyline, "", "");
82355682Smarkm	pw = NULL;
82455682Smarkm	logged_in = 0;
82555682Smarkm	guest = 0;
82655682Smarkm	dochroot = 0;
82755682Smarkm}
82855682Smarkm
82972445Sassar#ifdef KRB5
83072445Sassarstatic int
83172445Sassarkrb5_verify(struct passwd *pwd, char *passwd)
83272445Sassar{
833233294Sstas   krb5_context context;
83472445Sassar   krb5_ccache  id;
83572445Sassar   krb5_principal princ;
83672445Sassar   krb5_error_code ret;
837233294Sstas
83872445Sassar   ret = krb5_init_context(&context);
83972445Sassar   if(ret)
84072445Sassar        return ret;
84172445Sassar
84272445Sassar  ret = krb5_parse_name(context, pwd->pw_name, &princ);
84372445Sassar  if(ret){
84472445Sassar        krb5_free_context(context);
84572445Sassar        return ret;
84672445Sassar  }
847233294Sstas  ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);
84872445Sassar  if(ret){
84972445Sassar        krb5_free_principal(context, princ);
85072445Sassar        krb5_free_context(context);
85172445Sassar        return ret;
85272445Sassar  }
85372445Sassar  ret = krb5_verify_user(context,
85472445Sassar                         princ,
85572445Sassar                         id,
85672445Sassar                         passwd,
85772445Sassar                         1,
85872445Sassar                         NULL);
85972445Sassar  krb5_free_principal(context, princ);
86072445Sassar  if (k_hasafs()) {
86172445Sassar      krb5_afslog_uid_home(context, id,NULL, NULL,pwd->pw_uid, pwd->pw_dir);
86272445Sassar  }
86372445Sassar  krb5_cc_destroy(context, id);
86472445Sassar  krb5_free_context (context);
865233294Sstas  if(ret)
86672445Sassar      return ret;
86772445Sassar  return 0;
86872445Sassar}
86972445Sassar#endif /* KRB5 */
87072445Sassar
87155682Smarkmvoid
87255682Smarkmpass(char *passwd)
87355682Smarkm{
87455682Smarkm	int rval;
87555682Smarkm
87655682Smarkm	/* some clients insists on sending a password */
87755682Smarkm	if (logged_in && askpasswd == 0){
87872445Sassar	    reply(230, "Password not necessary");
87972445Sassar	    return;
88055682Smarkm	}
88155682Smarkm
88255682Smarkm	if (logged_in || askpasswd == 0) {
88355682Smarkm		reply(503, "Login with USER first.");
88455682Smarkm		return;
88555682Smarkm	}
88655682Smarkm	askpasswd = 0;
88755682Smarkm	rval = 1;
88855682Smarkm	if (!guest) {		/* "ftp" is only account allowed no password */
88955682Smarkm		if (pw == NULL)
89055682Smarkm			rval = 1;	/* failure below */
89155682Smarkm#ifdef OTP
89255682Smarkm		else if (otp_verify_user (&otp_ctx, passwd) == 0) {
89355682Smarkm		    rval = 0;
89455682Smarkm		}
89555682Smarkm#endif
89655682Smarkm		else if((auth_level & AUTH_OTP) == 0) {
89772445Sassar#ifdef KRB5
89872445Sassar		    rval = krb5_verify(pw, passwd);
89972445Sassar#endif
90072445Sassar		    if (rval)
90155682Smarkm			rval = unix_verify_user(pw->pw_name, passwd);
90255682Smarkm		} else {
903178825Sdfr#ifdef OTP
90455682Smarkm		    char *s;
90555682Smarkm		    if ((s = otp_error(&otp_ctx)) != NULL)
90655682Smarkm			lreply(530, "OTP: %s", s);
90755682Smarkm#endif
90855682Smarkm		}
90955682Smarkm		memset (passwd, 0, strlen(passwd));
91055682Smarkm
91155682Smarkm		/*
91255682Smarkm		 * If rval == 1, the user failed the authentication
91355682Smarkm		 * check above.  If rval == 0, either Kerberos or
91455682Smarkm		 * local authentication succeeded.
91555682Smarkm		 */
91655682Smarkm		if (rval) {
91755682Smarkm			char data_addr[256];
91855682Smarkm
91955682Smarkm			if (inet_ntop (his_addr->sa_family,
92055682Smarkm				       socket_get_address(his_addr),
92155682Smarkm				       data_addr, sizeof(data_addr)) == NULL)
92255682Smarkm				strlcpy (data_addr, "unknown address",
92355682Smarkm						 sizeof(data_addr));
92455682Smarkm
92555682Smarkm			reply(530, "Login incorrect.");
92655682Smarkm			if (logging)
92755682Smarkm				syslog(LOG_NOTICE,
92855682Smarkm				    "FTP LOGIN FAILED FROM %s(%s), %s",
92955682Smarkm				       remotehost,
93055682Smarkm				       data_addr,
93155682Smarkm				       curname);
93255682Smarkm			pw = NULL;
93355682Smarkm			if (login_attempts++ >= 5) {
93455682Smarkm				syslog(LOG_NOTICE,
93555682Smarkm				       "repeated login failures from %s(%s)",
93655682Smarkm				       remotehost,
93755682Smarkm				       data_addr);
93855682Smarkm				exit(0);
93955682Smarkm			}
94055682Smarkm			return;
94155682Smarkm		}
94255682Smarkm	}
94355682Smarkm	if(!do_login(230, passwd))
94455682Smarkm	  return;
945233294Sstas
94655682Smarkm	/* Forget all about it... */
94755682Smarkm	end_login();
94855682Smarkm}
94955682Smarkm
95055682Smarkmvoid
95155682Smarkmretrieve(const char *cmd, char *name)
95255682Smarkm{
95355682Smarkm	FILE *fin = NULL, *dout;
95455682Smarkm	struct stat st;
95555682Smarkm	int (*closefunc) (FILE *);
95655682Smarkm	char line[BUFSIZ];
95755682Smarkm
95855682Smarkm
95955682Smarkm	if (cmd == 0) {
96055682Smarkm		fin = fopen(name, "r");
96155682Smarkm		closefunc = fclose;
96255682Smarkm		st.st_size = 0;
96355682Smarkm		if(fin == NULL){
96455682Smarkm		    int save_errno = errno;
96555682Smarkm		    struct cmds {
96655682Smarkm			const char *ext;
96755682Smarkm			const char *cmd;
96855682Smarkm		        const char *rev_cmd;
96955682Smarkm		    } cmds[] = {
97055682Smarkm			{".tar", "/bin/gtar cPf - %s", NULL},
97155682Smarkm			{".tar.gz", "/bin/gtar zcPf - %s", NULL},
97255682Smarkm			{".tar.Z", "/bin/gtar ZcPf - %s", NULL},
97355682Smarkm			{".gz", "/bin/gzip -c -- %s", "/bin/gzip -c -d -- %s"},
97455682Smarkm			{".Z", "/bin/compress -c -- %s", "/bin/uncompress -c -- %s"},
97555682Smarkm			{NULL, NULL}
97655682Smarkm		    };
97755682Smarkm		    struct cmds *p;
97855682Smarkm		    for(p = cmds; p->ext; p++){
97955682Smarkm			char *tail = name + strlen(name) - strlen(p->ext);
98055682Smarkm			char c = *tail;
981233294Sstas
98255682Smarkm			if(strcmp(tail, p->ext) == 0 &&
98355682Smarkm			   (*tail  = 0) == 0 &&
98455682Smarkm			   access(name, R_OK) == 0){
98555682Smarkm			    snprintf (line, sizeof(line), p->cmd, name);
98655682Smarkm			    *tail  = c;
98755682Smarkm			    break;
98855682Smarkm			}
98955682Smarkm			*tail = c;
99055682Smarkm			if (p->rev_cmd != NULL) {
99155682Smarkm			    char *ext;
992178825Sdfr			    int ret;
99355682Smarkm
994178825Sdfr			    ret = asprintf(&ext, "%s%s", name, p->ext);
995178825Sdfr			    if (ret != -1) {
99655682Smarkm  			        if (access(ext, R_OK) == 0) {
99755682Smarkm				    snprintf (line, sizeof(line),
99855682Smarkm					      p->rev_cmd, ext);
99955682Smarkm				    free(ext);
100055682Smarkm				    break;
100155682Smarkm				}
100255682Smarkm			        free(ext);
100355682Smarkm			    }
100455682Smarkm			}
1005233294Sstas
100655682Smarkm		    }
100755682Smarkm		    if(p->ext){
100855682Smarkm			fin = ftpd_popen(line, "r", 0, 0);
100955682Smarkm			closefunc = ftpd_pclose;
101055682Smarkm			st.st_size = -1;
101155682Smarkm			cmd = line;
101255682Smarkm		    } else
101355682Smarkm			errno = save_errno;
101455682Smarkm		}
101555682Smarkm	} else {
101655682Smarkm		snprintf(line, sizeof(line), cmd, name);
101755682Smarkm		name = line;
101855682Smarkm		fin = ftpd_popen(line, "r", 1, 0);
101955682Smarkm		closefunc = ftpd_pclose;
102055682Smarkm		st.st_size = -1;
102155682Smarkm	}
102255682Smarkm	if (fin == NULL) {
102355682Smarkm		if (errno != 0) {
102455682Smarkm			perror_reply(550, name);
102555682Smarkm			if (cmd == 0) {
102655682Smarkm				LOGCMD("get", name);
102755682Smarkm			}
102855682Smarkm		}
102955682Smarkm		return;
103055682Smarkm	}
103155682Smarkm	byte_count = -1;
103255682Smarkm	if (cmd == 0){
103355682Smarkm	    if(fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) {
103455682Smarkm		reply(550, "%s: not a plain file.", name);
103555682Smarkm		goto done;
103655682Smarkm	    }
103755682Smarkm	}
103855682Smarkm	if (restart_point) {
103955682Smarkm		if (type == TYPE_A) {
104055682Smarkm			off_t i, n;
104155682Smarkm			int c;
104255682Smarkm
104355682Smarkm			n = restart_point;
104455682Smarkm			i = 0;
104555682Smarkm			while (i++ < n) {
104655682Smarkm				if ((c=getc(fin)) == EOF) {
104755682Smarkm					perror_reply(550, name);
104855682Smarkm					goto done;
104955682Smarkm				}
105055682Smarkm				if (c == '\n')
105155682Smarkm					i++;
105255682Smarkm			}
105355682Smarkm		} else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
105455682Smarkm			perror_reply(550, name);
105555682Smarkm			goto done;
105655682Smarkm		}
105755682Smarkm	}
105855682Smarkm	dout = dataconn(name, st.st_size, "w");
105955682Smarkm	if (dout == NULL)
106055682Smarkm		goto done;
106155682Smarkm	set_buffer_size(fileno(dout), 0);
106255682Smarkm	send_data(fin, dout);
106355682Smarkm	fclose(dout);
106455682Smarkm	data = -1;
106555682Smarkm	pdata = -1;
106655682Smarkmdone:
106755682Smarkm	if (cmd == 0)
106855682Smarkm		LOGBYTES("get", name, byte_count);
106955682Smarkm	(*closefunc)(fin);
107055682Smarkm}
107155682Smarkm
107255682Smarkm/* filename sanity check */
107355682Smarkm
1074233294Sstasint
107555682Smarkmfilename_check(char *filename)
107655682Smarkm{
1077178825Sdfr    char *p;
107855682Smarkm
1079178825Sdfr    p = strrchr(filename, '/');
108055682Smarkm    if(p)
108155682Smarkm	filename = p + 1;
108255682Smarkm
108355682Smarkm    p = filename;
108455682Smarkm
1085178825Sdfr    if(isalnum((unsigned char)*p)){
108655682Smarkm	p++;
1087178825Sdfr	while(*p && (isalnum((unsigned char)*p) || strchr(good_chars, (unsigned char)*p)))
108855682Smarkm	    p++;
108955682Smarkm	if(*p == '\0')
109055682Smarkm	    return 0;
109155682Smarkm    }
109272445Sassar    lreply(553, "\"%s\" is not an acceptable filename.", filename);
109355682Smarkm    lreply(553, "The filename must start with an alphanumeric "
109455682Smarkm	   "character and must only");
1095233294Sstas    reply(553, "consist of alphanumeric characters or any of the following: %s",
109655682Smarkm	  good_chars);
109755682Smarkm    return 1;
109855682Smarkm}
109955682Smarkm
110055682Smarkmvoid
110155682Smarkmdo_store(char *name, char *mode, int unique)
110255682Smarkm{
110355682Smarkm	FILE *fout, *din;
110455682Smarkm	struct stat st;
110555682Smarkm	int (*closefunc) (FILE *);
110655682Smarkm
110755682Smarkm	if(guest && filename_check(name))
110855682Smarkm	    return;
1109233294Sstas	if (unique) {
1110233294Sstas	    char *uname;
1111233294Sstas	    if (stat(name, &st) == 0) {
1112233294Sstas		if ((uname = gunique(name)) == NULL)
1113233294Sstas		    return;
1114233294Sstas		name = uname;
1115233294Sstas	    }
1116233294Sstas	    LOGCMD(*mode == 'w' ? "put" : "append", name);
111755682Smarkm	}
111855682Smarkm
111955682Smarkm	if (restart_point)
112055682Smarkm		mode = "r+";
112155682Smarkm	fout = fopen(name, mode);
112255682Smarkm	closefunc = fclose;
112355682Smarkm	if (fout == NULL) {
112455682Smarkm		perror_reply(553, name);
112555682Smarkm		LOGCMD(*mode == 'w' ? "put" : "append", name);
112655682Smarkm		return;
112755682Smarkm	}
112855682Smarkm	byte_count = -1;
112955682Smarkm	if (restart_point) {
113055682Smarkm		if (type == TYPE_A) {
113155682Smarkm			off_t i, n;
113255682Smarkm			int c;
113355682Smarkm
113455682Smarkm			n = restart_point;
113555682Smarkm			i = 0;
113655682Smarkm			while (i++ < n) {
113755682Smarkm				if ((c=getc(fout)) == EOF) {
113855682Smarkm					perror_reply(550, name);
113955682Smarkm					goto done;
114055682Smarkm				}
114155682Smarkm				if (c == '\n')
114255682Smarkm					i++;
114355682Smarkm			}
114455682Smarkm			/*
114555682Smarkm			 * We must do this seek to "current" position
114655682Smarkm			 * because we are changing from reading to
114755682Smarkm			 * writing.
114855682Smarkm			 */
114955682Smarkm			if (fseek(fout, 0L, SEEK_CUR) < 0) {
115055682Smarkm				perror_reply(550, name);
115155682Smarkm				goto done;
115255682Smarkm			}
115355682Smarkm		} else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
115455682Smarkm			perror_reply(550, name);
115555682Smarkm			goto done;
115655682Smarkm		}
115755682Smarkm	}
115855682Smarkm	din = dataconn(name, (off_t)-1, "r");
115955682Smarkm	if (din == NULL)
116055682Smarkm		goto done;
116155682Smarkm	set_buffer_size(fileno(din), 1);
116255682Smarkm	if (receive_data(din, fout) == 0) {
116378527Sassar	    if((*closefunc)(fout) < 0)
116478527Sassar		perror_reply(552, name);
116578527Sassar	    else {
116655682Smarkm		if (unique)
116755682Smarkm			reply(226, "Transfer complete (unique file name:%s).",
116855682Smarkm			    name);
116955682Smarkm		else
117055682Smarkm			reply(226, "Transfer complete.");
117178527Sassar	    }
117278527Sassar	} else
117378527Sassar	    (*closefunc)(fout);
117455682Smarkm	fclose(din);
117555682Smarkm	data = -1;
117655682Smarkm	pdata = -1;
117755682Smarkmdone:
117855682Smarkm	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
117955682Smarkm}
118055682Smarkm
118155682Smarkmstatic FILE *
1182178825Sdfrgetdatasock(const char *mode, int domain)
118355682Smarkm{
118455682Smarkm	int s, t, tries;
118555682Smarkm
118655682Smarkm	if (data >= 0)
118755682Smarkm		return (fdopen(data, mode));
1188178825Sdfr	if (seteuid(0) < 0)
1189178825Sdfr		fatal("Failed to seteuid");
1190178825Sdfr	s = socket(domain, SOCK_STREAM, 0);
119155682Smarkm	if (s < 0)
119255682Smarkm		goto bad;
119355682Smarkm	socket_set_reuseaddr (s, 1);
119455682Smarkm	/* anchor socket to avoid multi-homing problems */
119555682Smarkm	socket_set_address_and_port (data_source,
119655682Smarkm				     socket_get_address (ctrl_addr),
119755682Smarkm				     socket_get_port (data_source));
119855682Smarkm
119955682Smarkm	for (tries = 1; ; tries++) {
120055682Smarkm		if (bind(s, data_source,
120155682Smarkm			 socket_sockaddr_size (data_source)) >= 0)
120255682Smarkm			break;
120355682Smarkm		if (errno != EADDRINUSE || tries > 10)
120455682Smarkm			goto bad;
120555682Smarkm		sleep(tries);
120655682Smarkm	}
1207178825Sdfr	if (seteuid(pw->pw_uid) < 0)
1208178825Sdfr		fatal("Failed to seteuid");
120955682Smarkm#ifdef IPTOS_THROUGHPUT
121055682Smarkm	socket_set_tos (s, IPTOS_THROUGHPUT);
121155682Smarkm#endif
121255682Smarkm	return (fdopen(s, mode));
121355682Smarkmbad:
121455682Smarkm	/* Return the real value of errno (close may change it) */
121555682Smarkm	t = errno;
1216178825Sdfr	if (seteuid((uid_t)pw->pw_uid) < 0)
1217178825Sdfr		fatal("Failed to seteuid");
121855682Smarkm	close(s);
121955682Smarkm	errno = t;
122055682Smarkm	return (NULL);
122155682Smarkm}
122255682Smarkm
122390926Snectarstatic int
1224233294Sstasaccept_with_timeout(int socket,
122590926Snectar		    struct sockaddr *address,
1226120945Snectar		    socklen_t *address_len,
122790926Snectar		    struct timeval *timeout)
122890926Snectar{
122990926Snectar    int ret;
123090926Snectar    fd_set rfd;
123190926Snectar    FD_ZERO(&rfd);
123290926Snectar    FD_SET(socket, &rfd);
123390926Snectar    ret = select(socket + 1, &rfd, NULL, NULL, timeout);
123490926Snectar    if(ret < 0)
123590926Snectar	return ret;
123690926Snectar    if(ret == 0) {
123790926Snectar	errno = ETIMEDOUT;
123890926Snectar	return -1;
123990926Snectar    }
124090926Snectar    return accept(socket, address, address_len);
124190926Snectar}
124290926Snectar
124355682Smarkmstatic FILE *
124455682Smarkmdataconn(const char *name, off_t size, const char *mode)
124555682Smarkm{
124655682Smarkm	char sizebuf[32];
124755682Smarkm	FILE *file;
1248178825Sdfr	int domain, retry = 0;
124955682Smarkm
125055682Smarkm	file_size = size;
125155682Smarkm	byte_count = 0;
125255682Smarkm	if (size >= 0)
125355682Smarkm	    snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", (long)size);
125455682Smarkm	else
125555682Smarkm	    *sizebuf = '\0';
125655682Smarkm	if (pdata >= 0) {
125755682Smarkm		struct sockaddr_storage from_ss;
125855682Smarkm		struct sockaddr *from = (struct sockaddr *)&from_ss;
125990926Snectar		struct timeval timeout;
126055682Smarkm		int s;
126172445Sassar		socklen_t fromlen = sizeof(from_ss);
126255682Smarkm
126390926Snectar		timeout.tv_sec = 15;
126490926Snectar		timeout.tv_usec = 0;
126590926Snectar		s = accept_with_timeout(pdata, from, &fromlen, &timeout);
126655682Smarkm		if (s < 0) {
126755682Smarkm			reply(425, "Can't open data connection.");
126855682Smarkm			close(pdata);
126955682Smarkm			pdata = -1;
127055682Smarkm			return (NULL);
127155682Smarkm		}
127255682Smarkm		close(pdata);
127355682Smarkm		pdata = s;
1274233294Sstas#if defined(IPTOS_THROUGHPUT)
1275233294Sstas		if (from->sa_family == AF_INET)
1276233294Sstas		    socket_set_tos(s, IPTOS_THROUGHPUT);
127755682Smarkm#endif
127855682Smarkm		reply(150, "Opening %s mode data connection for '%s'%s.",
127955682Smarkm		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
128055682Smarkm		return (fdopen(pdata, mode));
128155682Smarkm	}
128255682Smarkm	if (data >= 0) {
128355682Smarkm		reply(125, "Using existing data connection for '%s'%s.",
128455682Smarkm		    name, sizebuf);
128555682Smarkm		usedefault = 1;
128655682Smarkm		return (fdopen(data, mode));
128755682Smarkm	}
128855682Smarkm	if (usedefault)
128955682Smarkm		data_dest = his_addr;
129055682Smarkm	usedefault = 1;
1291233294Sstas	/*
1292178825Sdfr	 * Default to using the same socket type as the ctrl address,
1293178825Sdfr	 * unless we know the type of the data address.
1294178825Sdfr	 */
1295178825Sdfr	domain = data_dest->sa_family;
1296178825Sdfr	if (domain == PF_UNSPEC)
1297178825Sdfr	    domain = ctrl_addr->sa_family;
1298178825Sdfr
1299178825Sdfr	file = getdatasock(mode, domain);
130055682Smarkm	if (file == NULL) {
130155682Smarkm		char data_addr[256];
130255682Smarkm
130355682Smarkm		if (inet_ntop (data_source->sa_family,
130455682Smarkm			       socket_get_address(data_source),
130555682Smarkm			       data_addr, sizeof(data_addr)) == NULL)
130655682Smarkm			strlcpy (data_addr, "unknown address",
130755682Smarkm					 sizeof(data_addr));
130855682Smarkm
130955682Smarkm		reply(425, "Can't create data socket (%s,%d): %s.",
131055682Smarkm		      data_addr,
131155682Smarkm		      socket_get_port (data_source),
131255682Smarkm		      strerror(errno));
131355682Smarkm		return (NULL);
131455682Smarkm	}
131555682Smarkm	data = fileno(file);
131655682Smarkm	while (connect(data, data_dest,
131755682Smarkm		       socket_sockaddr_size(data_dest)) < 0) {
131855682Smarkm		if (errno == EADDRINUSE && retry < swaitmax) {
131955682Smarkm			sleep(swaitint);
132055682Smarkm			retry += swaitint;
132155682Smarkm			continue;
132255682Smarkm		}
132355682Smarkm		perror_reply(425, "Can't build data connection");
132455682Smarkm		fclose(file);
132555682Smarkm		data = -1;
132655682Smarkm		return (NULL);
132755682Smarkm	}
132855682Smarkm	reply(150, "Opening %s mode data connection for '%s'%s.",
132955682Smarkm	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
133055682Smarkm	return (file);
133155682Smarkm}
133255682Smarkm
133355682Smarkm/*
133455682Smarkm * Tranfer the contents of "instr" to "outstr" peer using the appropriate
133555682Smarkm * encapsulation of the data subject * to Mode, Structure, and Type.
133655682Smarkm *
133755682Smarkm * NB: Form isn't handled.
133855682Smarkm */
133955682Smarkmstatic void
134055682Smarkmsend_data(FILE *instr, FILE *outstr)
134155682Smarkm{
134255682Smarkm	int c, cnt, filefd, netfd;
134355682Smarkm	static char *buf;
134455682Smarkm	static size_t bufsize;
134555682Smarkm
1346142403Snectar	transflag = 1;
134755682Smarkm	switch (type) {
134855682Smarkm
134955682Smarkm	case TYPE_A:
135055682Smarkm	    while ((c = getc(instr)) != EOF) {
1351142403Snectar		if (urgflag && handleoobcmd())
1352142403Snectar		    return;
135355682Smarkm		byte_count++;
135455682Smarkm		if(c == '\n')
135555682Smarkm		    sec_putc('\r', outstr);
135655682Smarkm		sec_putc(c, outstr);
135755682Smarkm	    }
135855682Smarkm	    sec_fflush(outstr);
135955682Smarkm	    transflag = 0;
1360142403Snectar	    urgflag = 0;
136155682Smarkm	    if (ferror(instr))
136255682Smarkm		goto file_err;
136355682Smarkm	    if (ferror(outstr))
136455682Smarkm		goto data_err;
136555682Smarkm	    reply(226, "Transfer complete.");
136655682Smarkm	    return;
1367233294Sstas
136855682Smarkm	case TYPE_I:
136955682Smarkm	case TYPE_L:
1370142403Snectar#if 0 /* XXX handle urg flag */
137155682Smarkm#if defined(HAVE_MMAP) && !defined(NO_MMAP)
137255682Smarkm#ifndef MAP_FAILED
137355682Smarkm#define MAP_FAILED (-1)
137455682Smarkm#endif
137555682Smarkm	    {
137655682Smarkm		struct stat st;
137755682Smarkm		char *chunk;
137855682Smarkm		int in = fileno(instr);
1379233294Sstas		if(fstat(in, &st) == 0 && S_ISREG(st.st_mode)
138055682Smarkm		   && st.st_size > 0) {
138155682Smarkm		    /*
138255682Smarkm		     * mmap zero bytes has potential of loosing, don't do it.
138355682Smarkm		     */
138455682Smarkm		    chunk = mmap(0, st.st_size, PROT_READ,
138555682Smarkm				 MAP_SHARED, in, 0);
138655682Smarkm		    if((void *)chunk != (void *)MAP_FAILED) {
138755682Smarkm			cnt = st.st_size - restart_point;
138855682Smarkm			sec_write(fileno(outstr), chunk + restart_point, cnt);
138955682Smarkm			if (munmap(chunk, st.st_size) < 0)
139055682Smarkm			    warn ("munmap");
139155682Smarkm			sec_fflush(outstr);
139255682Smarkm			byte_count = cnt;
139355682Smarkm			transflag = 0;
1394142403Snectar			urgflag = 0;
139555682Smarkm		    }
139655682Smarkm		}
139755682Smarkm	    }
139855682Smarkm#endif
1399142403Snectar#endif
140055682Smarkm	if(transflag) {
140155682Smarkm	    struct stat st;
140255682Smarkm
140355682Smarkm	    netfd = fileno(outstr);
140455682Smarkm	    filefd = fileno(instr);
140555682Smarkm	    buf = alloc_buffer (buf, &bufsize,
140655682Smarkm				fstat(filefd, &st) >= 0 ? &st : NULL);
140755682Smarkm	    if (buf == NULL) {
140855682Smarkm		transflag = 0;
1409142403Snectar		urgflag = 0;
141055682Smarkm		perror_reply(451, "Local resource failure: malloc");
141155682Smarkm		return;
141255682Smarkm	    }
141355682Smarkm	    while ((cnt = read(filefd, buf, bufsize)) > 0 &&
1414142403Snectar		   sec_write(netfd, buf, cnt) == cnt) {
141555682Smarkm		byte_count += cnt;
1416142403Snectar		if (urgflag && handleoobcmd())
1417142403Snectar		    return;
1418142403Snectar	    }
141955682Smarkm	    sec_fflush(outstr); /* to end an encrypted stream */
142055682Smarkm	    transflag = 0;
1421142403Snectar	    urgflag = 0;
142255682Smarkm	    if (cnt != 0) {
142355682Smarkm		if (cnt < 0)
142455682Smarkm		    goto file_err;
142555682Smarkm		goto data_err;
142655682Smarkm	    }
142755682Smarkm	}
142855682Smarkm	reply(226, "Transfer complete.");
142955682Smarkm	return;
143055682Smarkm	default:
143155682Smarkm	    transflag = 0;
1432142403Snectar	    urgflag = 0;
143355682Smarkm	    reply(550, "Unimplemented TYPE %d in send_data", type);
143455682Smarkm	    return;
143555682Smarkm	}
143655682Smarkm
143755682Smarkmdata_err:
143855682Smarkm	transflag = 0;
1439142403Snectar	urgflag = 0;
144055682Smarkm	perror_reply(426, "Data connection");
144155682Smarkm	return;
144255682Smarkm
144355682Smarkmfile_err:
144455682Smarkm	transflag = 0;
1445142403Snectar	urgflag = 0;
144655682Smarkm	perror_reply(551, "Error on input file");
144755682Smarkm}
144855682Smarkm
144955682Smarkm/*
145055682Smarkm * Transfer data from peer to "outstr" using the appropriate encapulation of
145155682Smarkm * the data subject to Mode, Structure, and Type.
145255682Smarkm *
145355682Smarkm * N.B.: Form isn't handled.
145455682Smarkm */
145555682Smarkmstatic int
145655682Smarkmreceive_data(FILE *instr, FILE *outstr)
145755682Smarkm{
145855682Smarkm    int cnt, bare_lfs = 0;
145955682Smarkm    static char *buf;
146055682Smarkm    static size_t bufsize;
146155682Smarkm    struct stat st;
146255682Smarkm
1463142403Snectar    transflag = 1;
146455682Smarkm
146555682Smarkm    buf = alloc_buffer (buf, &bufsize,
146655682Smarkm			fstat(fileno(outstr), &st) >= 0 ? &st : NULL);
146755682Smarkm    if (buf == NULL) {
146855682Smarkm	transflag = 0;
1469142403Snectar	urgflag = 0;
147055682Smarkm	perror_reply(451, "Local resource failure: malloc");
147155682Smarkm	return -1;
147255682Smarkm    }
1473233294Sstas
147455682Smarkm    switch (type) {
147555682Smarkm
147655682Smarkm    case TYPE_I:
147755682Smarkm    case TYPE_L:
147855682Smarkm	while ((cnt = sec_read(fileno(instr), buf, bufsize)) > 0) {
147955682Smarkm	    if (write(fileno(outstr), buf, cnt) != cnt)
148055682Smarkm		goto file_err;
148155682Smarkm	    byte_count += cnt;
1482142403Snectar	    if (urgflag && handleoobcmd())
1483142403Snectar		return (-1);
148455682Smarkm	}
148555682Smarkm	if (cnt < 0)
148655682Smarkm	    goto data_err;
148755682Smarkm	transflag = 0;
1488142403Snectar	urgflag = 0;
148955682Smarkm	return (0);
149055682Smarkm
149155682Smarkm    case TYPE_E:
149255682Smarkm	reply(553, "TYPE E not implemented.");
149355682Smarkm	transflag = 0;
1494142403Snectar	urgflag = 0;
149555682Smarkm	return (-1);
149655682Smarkm
149755682Smarkm    case TYPE_A:
149855682Smarkm    {
149955682Smarkm	char *p, *q;
150055682Smarkm	int cr_flag = 0;
150155682Smarkm	while ((cnt = sec_read(fileno(instr),
1502233294Sstas				buf + cr_flag,
150355682Smarkm				bufsize - cr_flag)) > 0){
1504142403Snectar	    if (urgflag && handleoobcmd())
1505142403Snectar		return (-1);
150655682Smarkm	    byte_count += cnt;
150755682Smarkm	    cnt += cr_flag;
150855682Smarkm	    cr_flag = 0;
150955682Smarkm	    for(p = buf, q = buf; p < buf + cnt;) {
151055682Smarkm		if(*p == '\n')
151155682Smarkm		    bare_lfs++;
151255682Smarkm		if(*p == '\r') {
151355682Smarkm		    if(p == buf + cnt - 1){
151455682Smarkm			cr_flag = 1;
151555682Smarkm			p++;
151655682Smarkm			continue;
151755682Smarkm		    }else if(p[1] == '\n'){
151855682Smarkm			*q++ = '\n';
151955682Smarkm			p += 2;
152055682Smarkm			continue;
152155682Smarkm		    }
152255682Smarkm		}
152355682Smarkm		*q++ = *p++;
152455682Smarkm	    }
152555682Smarkm	    fwrite(buf, q - buf, 1, outstr);
152655682Smarkm	    if(cr_flag)
152755682Smarkm		buf[0] = '\r';
152855682Smarkm	}
152955682Smarkm	if(cr_flag)
153055682Smarkm	    putc('\r', outstr);
153155682Smarkm	fflush(outstr);
153255682Smarkm	if (ferror(instr))
153355682Smarkm	    goto data_err;
153455682Smarkm	if (ferror(outstr))
153555682Smarkm	    goto file_err;
153655682Smarkm	transflag = 0;
1537142403Snectar	urgflag = 0;
153855682Smarkm	if (bare_lfs) {
153955682Smarkm	    lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n"
154055682Smarkm		   "    File may not have transferred correctly.\r\n",
154155682Smarkm		   bare_lfs);
154255682Smarkm	}
154355682Smarkm	return (0);
154455682Smarkm    }
154555682Smarkm    default:
154655682Smarkm	reply(550, "Unimplemented TYPE %d in receive_data", type);
154755682Smarkm	transflag = 0;
1548142403Snectar	urgflag = 0;
154955682Smarkm	return (-1);
155055682Smarkm    }
1551233294Sstas
155255682Smarkmdata_err:
155355682Smarkm    transflag = 0;
1554142403Snectar    urgflag = 0;
155555682Smarkm    perror_reply(426, "Data Connection");
155655682Smarkm    return (-1);
1557233294Sstas
155855682Smarkmfile_err:
155955682Smarkm    transflag = 0;
1560142403Snectar    urgflag = 0;
156155682Smarkm    perror_reply(452, "Error writing file");
156255682Smarkm    return (-1);
156355682Smarkm}
156455682Smarkm
156555682Smarkmvoid
156655682Smarkmstatfilecmd(char *filename)
156755682Smarkm{
156855682Smarkm	FILE *fin;
156955682Smarkm	int c;
157055682Smarkm	char line[LINE_MAX];
157155682Smarkm
157255682Smarkm	snprintf(line, sizeof(line), "/bin/ls -la -- %s", filename);
157355682Smarkm	fin = ftpd_popen(line, "r", 1, 0);
157455682Smarkm	lreply(211, "status of %s:", filename);
157555682Smarkm	while ((c = getc(fin)) != EOF) {
157655682Smarkm		if (c == '\n') {
157755682Smarkm			if (ferror(stdout)){
157855682Smarkm				perror_reply(421, "control connection");
157955682Smarkm				ftpd_pclose(fin);
158055682Smarkm				dologout(1);
158155682Smarkm				/* NOTREACHED */
158255682Smarkm			}
158355682Smarkm			if (ferror(fin)) {
158455682Smarkm				perror_reply(551, filename);
158555682Smarkm				ftpd_pclose(fin);
158655682Smarkm				return;
158755682Smarkm			}
158855682Smarkm			putc('\r', stdout);
158955682Smarkm		}
159055682Smarkm		putc(c, stdout);
159155682Smarkm	}
159255682Smarkm	ftpd_pclose(fin);
159355682Smarkm	reply(211, "End of Status");
159455682Smarkm}
159555682Smarkm
159655682Smarkmvoid
159755682Smarkmstatcmd(void)
159855682Smarkm{
159955682Smarkm#if 0
160055682Smarkm	struct sockaddr_in *sin;
160155682Smarkm	u_char *a, *p;
160255682Smarkm
160355682Smarkm	lreply(211, "%s FTP server (%s) status:", hostname, version);
160455682Smarkm	printf("     %s\r\n", version);
160555682Smarkm	printf("     Connected to %s", remotehost);
1606178825Sdfr	if (!isdigit((unsigned char)remotehost[0]))
160755682Smarkm		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
160855682Smarkm	printf("\r\n");
160955682Smarkm	if (logged_in) {
161055682Smarkm		if (guest)
161155682Smarkm			printf("     Logged in anonymously\r\n");
161255682Smarkm		else
161355682Smarkm			printf("     Logged in as %s\r\n", pw->pw_name);
161455682Smarkm	} else if (askpasswd)
161555682Smarkm		printf("     Waiting for password\r\n");
161655682Smarkm	else
161755682Smarkm		printf("     Waiting for user name\r\n");
161855682Smarkm	printf("     TYPE: %s", typenames[type]);
161955682Smarkm	if (type == TYPE_A || type == TYPE_E)
162055682Smarkm		printf(", FORM: %s", formnames[form]);
162155682Smarkm	if (type == TYPE_L)
162255682Smarkm#if NBBY == 8
162355682Smarkm		printf(" %d", NBBY);
162455682Smarkm#else
162555682Smarkm		printf(" %d", bytesize);	/* need definition! */
162655682Smarkm#endif
162755682Smarkm	printf("; STRUcture: %s; transfer MODE: %s\r\n",
162855682Smarkm	    strunames[stru], modenames[mode]);
162955682Smarkm	if (data != -1)
163055682Smarkm		printf("     Data connection open\r\n");
163155682Smarkm	else if (pdata != -1) {
163255682Smarkm		printf("     in Passive mode");
163355682Smarkm		sin = &pasv_addr;
163455682Smarkm		goto printaddr;
163555682Smarkm	} else if (usedefault == 0) {
163655682Smarkm		printf("     PORT");
163755682Smarkm		sin = &data_dest;
163855682Smarkmprintaddr:
163955682Smarkm		a = (u_char *) &sin->sin_addr;
164055682Smarkm		p = (u_char *) &sin->sin_port;
164155682Smarkm#define UC(b) (((int) b) & 0xff)
164255682Smarkm		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
164355682Smarkm			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
164455682Smarkm#undef UC
164555682Smarkm	} else
164655682Smarkm		printf("     No data connection\r\n");
164755682Smarkm#endif
164855682Smarkm	reply(211, "End of status");
164955682Smarkm}
165055682Smarkm
165155682Smarkmvoid
165255682Smarkmfatal(char *s)
165355682Smarkm{
165455682Smarkm
165555682Smarkm	reply(451, "Error in server: %s\n", s);
165655682Smarkm	reply(221, "Closing connection due to server error.");
165755682Smarkm	dologout(0);
165855682Smarkm	/* NOTREACHED */
165955682Smarkm}
166055682Smarkm
166155682Smarkmstatic void
166255682Smarkmint_reply(int, char *, const char *, va_list)
166355682Smarkm#ifdef __GNUC__
166455682Smarkm__attribute__ ((format (printf, 3, 0)))
166555682Smarkm#endif
166655682Smarkm;
166755682Smarkm
166855682Smarkmstatic void
166955682Smarkmint_reply(int n, char *c, const char *fmt, va_list ap)
167055682Smarkm{
167155682Smarkm    char buf[10240];
167255682Smarkm    char *p;
167355682Smarkm    p=buf;
167455682Smarkm    if(n){
167555682Smarkm	snprintf(p, sizeof(buf), "%d%s", n, c);
167655682Smarkm	p+=strlen(p);
167755682Smarkm    }
167855682Smarkm    vsnprintf(p, sizeof(buf) - strlen(p), fmt, ap);
167955682Smarkm    p+=strlen(p);
168055682Smarkm    snprintf(p, sizeof(buf) - strlen(p), "\r\n");
168155682Smarkm    p+=strlen(p);
168255682Smarkm    sec_fprintf(stdout, "%s", buf);
168355682Smarkm    fflush(stdout);
168455682Smarkm    if (debug)
168555682Smarkm	syslog(LOG_DEBUG, "<--- %s- ", buf);
168655682Smarkm}
168755682Smarkm
168855682Smarkmvoid
168955682Smarkmreply(int n, const char *fmt, ...)
169055682Smarkm{
169155682Smarkm  va_list ap;
169255682Smarkm  va_start(ap, fmt);
169355682Smarkm  int_reply(n, " ", fmt, ap);
169455682Smarkm  delete_ftp_command();
169555682Smarkm  va_end(ap);
169655682Smarkm}
169755682Smarkm
169855682Smarkmvoid
169955682Smarkmlreply(int n, const char *fmt, ...)
170055682Smarkm{
170155682Smarkm  va_list ap;
170255682Smarkm  va_start(ap, fmt);
170355682Smarkm  int_reply(n, "-", fmt, ap);
170455682Smarkm  va_end(ap);
170555682Smarkm}
170655682Smarkm
170755682Smarkmvoid
170855682Smarkmnreply(const char *fmt, ...)
170955682Smarkm{
171055682Smarkm  va_list ap;
171155682Smarkm  va_start(ap, fmt);
171255682Smarkm  int_reply(0, NULL, fmt, ap);
171355682Smarkm  va_end(ap);
171455682Smarkm}
171555682Smarkm
171655682Smarkmstatic void
171755682Smarkmack(char *s)
171855682Smarkm{
171955682Smarkm
172055682Smarkm	reply(250, "%s command successful.", s);
172155682Smarkm}
172255682Smarkm
172355682Smarkmvoid
172455682Smarkmnack(char *s)
172555682Smarkm{
172655682Smarkm
172755682Smarkm	reply(502, "%s command not implemented.", s);
172855682Smarkm}
172955682Smarkm
173055682Smarkmvoid
173155682Smarkmdo_delete(char *name)
173255682Smarkm{
173355682Smarkm	struct stat st;
173455682Smarkm
173555682Smarkm	LOGCMD("delete", name);
173655682Smarkm	if (stat(name, &st) < 0) {
173755682Smarkm		perror_reply(550, name);
173855682Smarkm		return;
173955682Smarkm	}
1740233294Sstas	if (S_ISDIR(st.st_mode)) {
174155682Smarkm		if (rmdir(name) < 0) {
174255682Smarkm			perror_reply(550, name);
174355682Smarkm			return;
174455682Smarkm		}
174555682Smarkm		goto done;
174655682Smarkm	}
174755682Smarkm	if (unlink(name) < 0) {
174855682Smarkm		perror_reply(550, name);
174955682Smarkm		return;
175055682Smarkm	}
175155682Smarkmdone:
175255682Smarkm	ack("DELE");
175355682Smarkm}
175455682Smarkm
175555682Smarkmvoid
1756233294Sstascwd(const char *path)
175755682Smarkm{
175855682Smarkm
175955682Smarkm	if (chdir(path) < 0)
176055682Smarkm		perror_reply(550, path);
176155682Smarkm	else
176255682Smarkm		ack("CWD");
176355682Smarkm}
176455682Smarkm
176555682Smarkmvoid
176655682Smarkmmakedir(char *name)
176755682Smarkm{
176855682Smarkm
176955682Smarkm	LOGCMD("mkdir", name);
177055682Smarkm	if(guest && filename_check(name))
177155682Smarkm	    return;
177255682Smarkm	if (mkdir(name, 0777) < 0)
177355682Smarkm		perror_reply(550, name);
177455682Smarkm	else{
177555682Smarkm	    if(guest)
177655682Smarkm		chmod(name, 0700); /* guest has umask 777 */
177755682Smarkm	    reply(257, "MKD command successful.");
177855682Smarkm	}
177955682Smarkm}
178055682Smarkm
178155682Smarkmvoid
178255682Smarkmremovedir(char *name)
178355682Smarkm{
178455682Smarkm
178555682Smarkm	LOGCMD("rmdir", name);
178655682Smarkm	if (rmdir(name) < 0)
178755682Smarkm		perror_reply(550, name);
178855682Smarkm	else
178955682Smarkm		ack("RMD");
179055682Smarkm}
179155682Smarkm
179255682Smarkmvoid
179355682Smarkmpwd(void)
179455682Smarkm{
179555682Smarkm    char path[MaxPathLen];
179655682Smarkm    char *ret;
179755682Smarkm
179855682Smarkm    /* SunOS has a broken getcwd that does popen(pwd) (!!!), this
1799233294Sstas     * failes miserably when running chroot
180055682Smarkm     */
180155682Smarkm    ret = getcwd(path, sizeof(path));
180255682Smarkm    if (ret == NULL)
180355682Smarkm	reply(550, "%s.", strerror(errno));
180455682Smarkm    else
180555682Smarkm	reply(257, "\"%s\" is current directory.", path);
180655682Smarkm}
180755682Smarkm
180855682Smarkmchar *
180955682Smarkmrenamefrom(char *name)
181055682Smarkm{
181155682Smarkm	struct stat st;
181255682Smarkm
181355682Smarkm	if (stat(name, &st) < 0) {
181455682Smarkm		perror_reply(550, name);
181555682Smarkm		return NULL;
181655682Smarkm	}
181755682Smarkm	reply(350, "File exists, ready for destination name");
181855682Smarkm	return (name);
181955682Smarkm}
182055682Smarkm
182155682Smarkmvoid
182255682Smarkmrenamecmd(char *from, char *to)
182355682Smarkm{
182455682Smarkm
182555682Smarkm	LOGCMD2("rename", from, to);
182655682Smarkm	if(guest && filename_check(to))
182755682Smarkm	    return;
182855682Smarkm	if (rename(from, to) < 0)
182955682Smarkm		perror_reply(550, "rename");
183055682Smarkm	else
183155682Smarkm		ack("RNTO");
183255682Smarkm}
183355682Smarkm
183455682Smarkmstatic void
183555682Smarkmdolog(struct sockaddr *sa, int len)
183655682Smarkm{
183755682Smarkm	getnameinfo_verified (sa, len, remotehost, sizeof(remotehost),
183855682Smarkm			      NULL, 0, 0);
183955682Smarkm#ifdef HAVE_SETPROCTITLE
184055682Smarkm	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
184164593Skris	setproctitle("%s", proctitle);
184255682Smarkm#endif /* HAVE_SETPROCTITLE */
184355682Smarkm
184455682Smarkm	if (logging) {
184555682Smarkm		char data_addr[256];
184655682Smarkm
184755682Smarkm		if (inet_ntop (his_addr->sa_family,
184855682Smarkm			       socket_get_address(his_addr),
184955682Smarkm			       data_addr, sizeof(data_addr)) == NULL)
185055682Smarkm			strlcpy (data_addr, "unknown address",
185155682Smarkm					 sizeof(data_addr));
185255682Smarkm
185355682Smarkm
185455682Smarkm		syslog(LOG_INFO, "connection from %s(%s)",
185555682Smarkm		       remotehost,
185655682Smarkm		       data_addr);
185755682Smarkm	}
185855682Smarkm}
185955682Smarkm
186055682Smarkm/*
186155682Smarkm * Record logout in wtmp file
186255682Smarkm * and exit with supplied status.
186355682Smarkm */
186455682Smarkmvoid
186555682Smarkmdologout(int status)
186655682Smarkm{
186755682Smarkm    transflag = 0;
1868142403Snectar    urgflag = 0;
186955682Smarkm    if (logged_in) {
1870233294Sstas#if KRB5
187155682Smarkm	cond_kdestroy();
187255682Smarkm#endif
1873178825Sdfr	seteuid((uid_t)0); /* No need to check, we call exit() below */
1874178825Sdfr	ftpd_logwtmp(ttyline, "", "");
187555682Smarkm    }
187655682Smarkm    /* beware of flushing buffers after a SIGPIPE */
187755682Smarkm#ifdef XXX
187855682Smarkm    exit(status);
187955682Smarkm#else
188055682Smarkm    _exit(status);
1881233294Sstas#endif
188255682Smarkm}
188355682Smarkm
188455682Smarkmvoid abor(void)
188555682Smarkm{
1886142403Snectar    if (!transflag)
1887142403Snectar	return;
1888142403Snectar    reply(426, "Transfer aborted. Data connection closed.");
1889142403Snectar    reply(226, "Abort successful");
1890142403Snectar    transflag = 0;
189155682Smarkm}
189255682Smarkm
189355682Smarkmstatic void
189455682Smarkmmyoob(int signo)
189555682Smarkm{
1896142403Snectar    urgflag = 1;
1897142403Snectar}
1898142403Snectar
1899142403Snectarstatic char *
1900142403Snectarmec_space(char *p)
1901142403Snectar{
1902142403Snectar    while(isspace(*(unsigned char *)p))
1903142403Snectar	  p++;
1904142403Snectar    return p;
1905142403Snectar}
1906142403Snectar
1907142403Snectarstatic int
1908142403Snectarhandleoobcmd(void)
1909142403Snectar{
191055682Smarkm	char *cp;
191155682Smarkm
191255682Smarkm	/* only process if transfer occurring */
191355682Smarkm	if (!transflag)
1914142403Snectar		return 0;
191555682Smarkm
1916142403Snectar	urgflag = 0;
191755682Smarkm
191855682Smarkm	cp = tmpline;
1919142403Snectar	if (ftpd_getline(cp, sizeof(tmpline)) == NULL) {
192055682Smarkm		reply(221, "You could at least say goodbye.");
192155682Smarkm		dologout(0);
192255682Smarkm	}
1923142403Snectar
1924142403Snectar	if (strncasecmp("MIC", cp, 3) == 0) {
1925142403Snectar	    mec(mec_space(cp + 3), prot_safe);
1926142403Snectar	} else if (strncasecmp("CONF", cp, 4) == 0) {
1927142403Snectar	    mec(mec_space(cp + 4), prot_confidential);
1928142403Snectar	} else if (strncasecmp("ENC", cp, 3) == 0) {
1929142403Snectar	    mec(mec_space(cp + 3), prot_private);
1930142403Snectar	} else if (!allow_insecure_oob) {
1931142403Snectar	    reply(533, "Command protection level denied "
1932142403Snectar		  "for paranoid reasons.");
1933142403Snectar	    goto out;
193455682Smarkm	}
1935142403Snectar
1936142403Snectar	if (secure_command())
1937142403Snectar	    cp = ftp_command;
1938142403Snectar
1939142403Snectar	if (strcasecmp(cp, "ABOR\r\n") == 0) {
1940142403Snectar		abor();
1941142403Snectar	} else if (strcasecmp(cp, "STAT\r\n") == 0) {
194255682Smarkm		if (file_size != (off_t) -1)
194355682Smarkm			reply(213, "Status: %ld of %ld bytes transferred",
194455682Smarkm			      (long)byte_count,
194555682Smarkm			      (long)file_size);
194655682Smarkm		else
1947142403Snectar			reply(213, "Status: %ld bytes transferred",
194855682Smarkm			      (long)byte_count);
194955682Smarkm	}
1950142403Snectarout:
1951142403Snectar	return (transflag == 0);
195255682Smarkm}
195355682Smarkm
195455682Smarkm/*
195555682Smarkm * Note: a response of 425 is not mentioned as a possible response to
195655682Smarkm *	the PASV command in RFC959. However, it has been blessed as
195755682Smarkm *	a legitimate response by Jon Postel in a telephone conversation
195855682Smarkm *	with Rick Adams on 25 Jan 89.
195955682Smarkm */
196055682Smarkmvoid
196155682Smarkmpasv(void)
196255682Smarkm{
196372445Sassar	socklen_t len;
196455682Smarkm	char *p, *a;
196555682Smarkm	struct sockaddr_in *sin;
196655682Smarkm
196755682Smarkm	if (ctrl_addr->sa_family != AF_INET) {
196855682Smarkm		reply(425,
196955682Smarkm		      "You cannot do PASV with something that's not IPv4");
197055682Smarkm		return;
197155682Smarkm	}
197255682Smarkm
197372445Sassar	if(pdata != -1)
197472445Sassar	    close(pdata);
197572445Sassar
197655682Smarkm	pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
197755682Smarkm	if (pdata < 0) {
197855682Smarkm		perror_reply(425, "Can't open passive connection");
197955682Smarkm		return;
198055682Smarkm	}
198155682Smarkm	pasv_addr->sa_family = ctrl_addr->sa_family;
198255682Smarkm	socket_set_address_and_port (pasv_addr,
198355682Smarkm				     socket_get_address (ctrl_addr),
198455682Smarkm				     0);
1985233294Sstas	socket_set_portrange(pdata, restricted_data_ports,
1986233294Sstas	    pasv_addr->sa_family);
1987178825Sdfr	if (seteuid(0) < 0)
1988178825Sdfr		fatal("Failed to seteuid");
198955682Smarkm	if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
1990178825Sdfr		if (seteuid(pw->pw_uid) < 0)
1991178825Sdfr			fatal("Failed to seteuid");
199255682Smarkm		goto pasv_error;
199355682Smarkm	}
1994178825Sdfr	if (seteuid(pw->pw_uid) < 0)
1995178825Sdfr		fatal("Failed to seteuid");
199655682Smarkm	len = sizeof(pasv_addr_ss);
199755682Smarkm	if (getsockname(pdata, pasv_addr, &len) < 0)
199855682Smarkm		goto pasv_error;
199955682Smarkm	if (listen(pdata, 1) < 0)
200055682Smarkm		goto pasv_error;
200155682Smarkm	sin = (struct sockaddr_in *)pasv_addr;
200255682Smarkm	a = (char *) &sin->sin_addr;
200355682Smarkm	p = (char *) &sin->sin_port;
200455682Smarkm
200555682Smarkm#define UC(b) (((int) b) & 0xff)
200655682Smarkm
200755682Smarkm	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
200855682Smarkm		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
200955682Smarkm	return;
201055682Smarkm
201155682Smarkmpasv_error:
201255682Smarkm	close(pdata);
201355682Smarkm	pdata = -1;
201455682Smarkm	perror_reply(425, "Can't open passive connection");
201555682Smarkm	return;
201655682Smarkm}
201755682Smarkm
201855682Smarkmvoid
201955682Smarkmepsv(char *proto)
202055682Smarkm{
202172445Sassar	socklen_t len;
202255682Smarkm
202355682Smarkm	pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
202455682Smarkm	if (pdata < 0) {
202555682Smarkm		perror_reply(425, "Can't open passive connection");
202655682Smarkm		return;
202755682Smarkm	}
202855682Smarkm	pasv_addr->sa_family = ctrl_addr->sa_family;
202955682Smarkm	socket_set_address_and_port (pasv_addr,
203055682Smarkm				     socket_get_address (ctrl_addr),
203155682Smarkm				     0);
2032233294Sstas	socket_set_portrange(pdata, restricted_data_ports,
2033233294Sstas	    pasv_addr->sa_family);
2034178825Sdfr	if (seteuid(0) < 0)
2035178825Sdfr		fatal("Failed to seteuid");
203655682Smarkm	if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
2037178825Sdfr		if (seteuid(pw->pw_uid))
2038178825Sdfr			fatal("Failed to seteuid");
203955682Smarkm		goto pasv_error;
204055682Smarkm	}
2041178825Sdfr	if (seteuid(pw->pw_uid) < 0)
2042178825Sdfr		fatal("Failed to seteuid");
204355682Smarkm	len = sizeof(pasv_addr_ss);
204455682Smarkm	if (getsockname(pdata, pasv_addr, &len) < 0)
204555682Smarkm		goto pasv_error;
204655682Smarkm	if (listen(pdata, 1) < 0)
204755682Smarkm		goto pasv_error;
204855682Smarkm
204955682Smarkm	reply(229, "Entering Extended Passive Mode (|||%d|)",
205055682Smarkm	      ntohs(socket_get_port (pasv_addr)));
205155682Smarkm	return;
205255682Smarkm
205355682Smarkmpasv_error:
205455682Smarkm	close(pdata);
205555682Smarkm	pdata = -1;
205655682Smarkm	perror_reply(425, "Can't open passive connection");
205755682Smarkm	return;
205855682Smarkm}
205955682Smarkm
206055682Smarkmvoid
206155682Smarkmeprt(char *str)
206255682Smarkm{
206355682Smarkm	char *end;
206455682Smarkm	char sep;
206555682Smarkm	int af;
206655682Smarkm	int ret;
206755682Smarkm	int port;
206855682Smarkm
206955682Smarkm	usedefault = 0;
207055682Smarkm	if (pdata >= 0) {
207155682Smarkm	    close(pdata);
207255682Smarkm	    pdata = -1;
207355682Smarkm	}
207455682Smarkm
207555682Smarkm	sep = *str++;
207655682Smarkm	if (sep == '\0') {
207755682Smarkm		reply(500, "Bad syntax in EPRT");
207855682Smarkm		return;
207955682Smarkm	}
208055682Smarkm	af = strtol (str, &end, 0);
208155682Smarkm	if (af == 0 || *end != sep) {
208255682Smarkm		reply(500, "Bad syntax in EPRT");
208355682Smarkm		return;
208455682Smarkm	}
208555682Smarkm	str = end + 1;
208655682Smarkm	switch (af) {
208755682Smarkm#ifdef HAVE_IPV6
208855682Smarkm	case 2 :
208955682Smarkm	    data_dest->sa_family = AF_INET6;
209055682Smarkm	    break;
2091233294Sstas#endif
209255682Smarkm	case 1 :
209355682Smarkm	    data_dest->sa_family = AF_INET;
209455682Smarkm		break;
209555682Smarkm	default :
209655682Smarkm		reply(522, "Network protocol %d not supported, use (1"
209755682Smarkm#ifdef HAVE_IPV6
209855682Smarkm		      ",2"
209955682Smarkm#endif
210055682Smarkm		      ")", af);
210155682Smarkm		return;
210255682Smarkm	}
210355682Smarkm	end = strchr (str, sep);
210455682Smarkm	if (end == NULL) {
210555682Smarkm		reply(500, "Bad syntax in EPRT");
210655682Smarkm		return;
210755682Smarkm	}
210855682Smarkm	*end = '\0';
210955682Smarkm	ret = inet_pton (data_dest->sa_family, str,
211055682Smarkm			 socket_get_address (data_dest));
211155682Smarkm
211255682Smarkm	if (ret != 1) {
211355682Smarkm		reply(500, "Bad address syntax in EPRT");
211455682Smarkm		return;
211555682Smarkm	}
211655682Smarkm	str = end + 1;
211755682Smarkm	port = strtol (str, &end, 0);
211855682Smarkm	if (port == 0 || *end != sep) {
211955682Smarkm		reply(500, "Bad port syntax in EPRT");
212055682Smarkm		return;
212155682Smarkm	}
2122233294Sstas	if (port < IPPORT_RESERVED) {
2123233294Sstas		reply(500, "Bad port in invalid range in EPRT");
2124233294Sstas		return;
2125233294Sstas	}
212655682Smarkm	socket_set_port (data_dest, htons(port));
2127233294Sstas
2128233294Sstas	if (paranoid &&
2129233294Sstas	    (data_dest->sa_family != his_addr->sa_family ||
2130233294Sstas	     memcmp(socket_get_address(data_dest), socket_get_address(his_addr), socket_sockaddr_size(data_dest)) != 0))
2131233294Sstas	{
2132233294Sstas		reply(500, "Bad address in EPRT");
2133233294Sstas	}
213455682Smarkm	reply(200, "EPRT command successful.");
213555682Smarkm}
213655682Smarkm
213755682Smarkm/*
213855682Smarkm * Generate unique name for file with basename "local".
213955682Smarkm * The file named "local" is already known to exist.
214055682Smarkm * Generates failure reply on error.
214155682Smarkm */
214255682Smarkmstatic char *
214355682Smarkmgunique(char *local)
214455682Smarkm{
214555682Smarkm	static char new[MaxPathLen];
214655682Smarkm	struct stat st;
214755682Smarkm	int count;
214855682Smarkm	char *cp;
214955682Smarkm
215055682Smarkm	cp = strrchr(local, '/');
215155682Smarkm	if (cp)
215255682Smarkm		*cp = '\0';
215355682Smarkm	if (stat(cp ? local : ".", &st) < 0) {
215455682Smarkm		perror_reply(553, cp ? local : ".");
215555682Smarkm		return NULL;
215655682Smarkm	}
215755682Smarkm	if (cp)
215855682Smarkm		*cp = '/';
215955682Smarkm	for (count = 1; count < 100; count++) {
216055682Smarkm		snprintf (new, sizeof(new), "%s.%d", local, count);
216155682Smarkm		if (stat(new, &st) < 0)
216255682Smarkm			return (new);
216355682Smarkm	}
216455682Smarkm	reply(452, "Unique file name cannot be created.");
216555682Smarkm	return (NULL);
216655682Smarkm}
216755682Smarkm
216855682Smarkm/*
216955682Smarkm * Format and send reply containing system error number.
217055682Smarkm */
217155682Smarkmvoid
217255682Smarkmperror_reply(int code, const char *string)
217355682Smarkm{
217455682Smarkm	reply(code, "%s: %s.", string, strerror(errno));
217555682Smarkm}
217655682Smarkm
217755682Smarkmstatic char *onefile[] = {
217855682Smarkm	"",
217955682Smarkm	0
218055682Smarkm};
218155682Smarkm
218255682Smarkmvoid
218355682Smarkmlist_file(char *file)
218455682Smarkm{
218555682Smarkm    if(use_builtin_ls) {
218655682Smarkm	FILE *dout;
218755682Smarkm	dout = dataconn(file, -1, "w");
218855682Smarkm	if (dout == NULL)
218955682Smarkm	    return;
219055682Smarkm	set_buffer_size(fileno(dout), 0);
2191102644Snectar	if(builtin_ls(dout, file) == 0)
2192102644Snectar	    reply(226, "Transfer complete.");
2193102644Snectar	else
2194102644Snectar	    reply(451, "Requested action aborted. Local error in processing.");
219555682Smarkm	fclose(dout);
219655682Smarkm	data = -1;
219755682Smarkm	pdata = -1;
219855682Smarkm    } else {
219955682Smarkm#ifdef HAVE_LS_A
220072445Sassar	const char *cmd = "/bin/ls -lA %s";
220155682Smarkm#else
220272445Sassar	const char *cmd = "/bin/ls -la %s";
220355682Smarkm#endif
220455682Smarkm	retrieve(cmd, file);
220555682Smarkm    }
220655682Smarkm}
220755682Smarkm
220855682Smarkmvoid
220955682Smarkmsend_file_list(char *whichf)
221055682Smarkm{
2211142403Snectar    struct stat st;
2212142403Snectar    DIR *dirp = NULL;
2213142403Snectar    struct dirent *dir;
2214142403Snectar    FILE *dout = NULL;
2215142403Snectar    char **dirlist, *dirname;
2216142403Snectar    int simple = 0;
2217142403Snectar    int freeglob = 0;
2218142403Snectar    glob_t gl;
2219142403Snectar    char buf[MaxPathLen];
222055682Smarkm
2221142403Snectar    if (strpbrk(whichf, "~{[*?") != NULL) {
2222142403Snectar	int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|
222390926Snectar#ifdef GLOB_MAXPATH
2224142403Snectar	    GLOB_MAXPATH
222590926Snectar#else
2226142403Snectar	    GLOB_LIMIT
222790926Snectar#endif
2228142403Snectar	    ;
222955682Smarkm
2230142403Snectar	memset(&gl, 0, sizeof(gl));
2231142403Snectar	freeglob = 1;
2232142403Snectar	if (glob(whichf, flags, 0, &gl)) {
2233142403Snectar	    reply(550, "not found");
2234142403Snectar	    goto out;
2235142403Snectar	} else if (gl.gl_pathc == 0) {
2236142403Snectar	    errno = ENOENT;
2237142403Snectar	    perror_reply(550, whichf);
2238142403Snectar	    goto out;
2239142403Snectar	}
2240142403Snectar	dirlist = gl.gl_pathv;
2241142403Snectar    } else {
2242142403Snectar	onefile[0] = whichf;
2243142403Snectar	dirlist = onefile;
2244142403Snectar	simple = 1;
224555682Smarkm    }
224655682Smarkm
2247142403Snectar    while ((dirname = *dirlist++)) {
224855682Smarkm
2249142403Snectar	if (urgflag && handleoobcmd())
2250142403Snectar	    goto out;
225155682Smarkm
2252142403Snectar	if (stat(dirname, &st) < 0) {
2253142403Snectar	    /*
2254142403Snectar	     * If user typed "ls -l", etc, and the client
2255142403Snectar	     * used NLST, do what the user meant.
2256142403Snectar	     */
2257142403Snectar	    if (dirname[0] == '-' && *dirlist == NULL &&
2258142403Snectar		transflag == 0) {
2259142403Snectar		list_file(dirname);
2260142403Snectar		goto out;
2261142403Snectar	    }
2262142403Snectar	    perror_reply(550, whichf);
2263142403Snectar	    goto out;
2264142403Snectar	}
226555682Smarkm
2266142403Snectar	if (S_ISREG(st.st_mode)) {
2267142403Snectar	    if (dout == NULL) {
2268142403Snectar		dout = dataconn("file list", (off_t)-1, "w");
2269142403Snectar		if (dout == NULL)
2270142403Snectar		    goto out;
2271142403Snectar		transflag = 1;
2272142403Snectar	    }
2273142403Snectar	    snprintf(buf, sizeof(buf), "%s%s\n", dirname,
2274142403Snectar		     type == TYPE_A ? "\r" : "");
2275142403Snectar	    sec_write(fileno(dout), buf, strlen(buf));
2276142403Snectar	    byte_count += strlen(dirname) + 1;
2277142403Snectar	    continue;
2278142403Snectar	} else if (!S_ISDIR(st.st_mode))
2279142403Snectar	    continue;
228055682Smarkm
2281142403Snectar	if ((dirp = opendir(dirname)) == NULL)
2282142403Snectar	    continue;
228355682Smarkm
2284142403Snectar	while ((dir = readdir(dirp)) != NULL) {
2285142403Snectar	    char nbuf[MaxPathLen];
228655682Smarkm
2287142403Snectar	    if (urgflag && handleoobcmd())
2288142403Snectar		goto out;
2289142403Snectar
2290142403Snectar	    if (!strcmp(dir->d_name, "."))
2291142403Snectar		continue;
2292142403Snectar	    if (!strcmp(dir->d_name, ".."))
2293142403Snectar		continue;
2294142403Snectar
2295142403Snectar	    snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
2296142403Snectar
2297142403Snectar	    /*
2298142403Snectar	     * We have to do a stat to insure it's
2299142403Snectar	     * not a directory or special file.
2300142403Snectar	     */
2301142403Snectar	    if (simple || (stat(nbuf, &st) == 0 &&
2302142403Snectar			   S_ISREG(st.st_mode))) {
2303142403Snectar		if (dout == NULL) {
2304142403Snectar		    dout = dataconn("file list", (off_t)-1, "w");
2305142403Snectar		    if (dout == NULL)
2306142403Snectar			goto out;
2307142403Snectar		    transflag = 1;
2308142403Snectar		}
2309142403Snectar		if(strncmp(nbuf, "./", 2) == 0)
2310142403Snectar		    snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2,
2311142403Snectar			     type == TYPE_A ? "\r" : "");
2312142403Snectar		else
2313142403Snectar		    snprintf(buf, sizeof(buf), "%s%s\n", nbuf,
2314142403Snectar			     type == TYPE_A ? "\r" : "");
2315142403Snectar		sec_write(fileno(dout), buf, strlen(buf));
2316142403Snectar		byte_count += strlen(nbuf) + 1;
2317142403Snectar	    }
231855682Smarkm	}
2319142403Snectar	closedir(dirp);
232055682Smarkm    }
2321142403Snectar    if (dout == NULL)
2322142403Snectar	reply(550, "No files found.");
2323142403Snectar    else if (ferror(dout) != 0)
2324142403Snectar	perror_reply(550, "Data connection");
2325142403Snectar    else
2326142403Snectar	reply(226, "Transfer complete.");
232755682Smarkm
2328142403Snectarout:
2329142403Snectar    transflag = 0;
2330142403Snectar    if (dout != NULL){
2331142403Snectar	sec_write(fileno(dout), buf, 0); /* XXX flush */
2332233294Sstas
2333142403Snectar	fclose(dout);
2334142403Snectar    }
2335142403Snectar    data = -1;
2336142403Snectar    pdata = -1;
2337233294Sstas    if (freeglob)
2338142403Snectar	globfree(&gl);
233955682Smarkm}
234055682Smarkm
234155682Smarkm
234255682Smarkmint
234355682Smarkmfind(char *pattern)
234455682Smarkm{
234555682Smarkm    char line[1024];
234655682Smarkm    FILE *f;
234755682Smarkm
234855682Smarkm    snprintf(line, sizeof(line),
234955682Smarkm	     "/bin/locate -d %s -- %s",
235055682Smarkm	     ftp_rooted("/etc/locatedb"),
235155682Smarkm	     pattern);
235255682Smarkm    f = ftpd_popen(line, "r", 1, 1);
235355682Smarkm    if(f == NULL){
235455682Smarkm	perror_reply(550, "/bin/locate");
235555682Smarkm	return 1;
235655682Smarkm    }
235755682Smarkm    lreply(200, "Output from find.");
235855682Smarkm    while(fgets(line, sizeof(line), f)){
235955682Smarkm	if(line[strlen(line)-1] == '\n')
236055682Smarkm	    line[strlen(line)-1] = 0;
236155682Smarkm	nreply("%s", line);
236255682Smarkm    }
236355682Smarkm    reply(200, "Done");
236455682Smarkm    ftpd_pclose(f);
236555682Smarkm    return 0;
236655682Smarkm}
236755682Smarkm
2368