xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ftpd.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2b528cefcSMark Murray  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
3b528cefcSMark Murray  *	The Regents of the University of California.  All rights reserved.
4b528cefcSMark Murray  *
5b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
6b528cefcSMark Murray  * modification, are permitted provided that the following conditions
7b528cefcSMark Murray  * are met:
8b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
9b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
10b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
12b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
13b528cefcSMark Murray  * 3. All advertising materials mentioning features or use of this software
14b528cefcSMark Murray  *    must display the following acknowledgement:
15b528cefcSMark Murray  *	This product includes software developed by the University of
16b528cefcSMark Murray  *	California, Berkeley and its contributors.
17b528cefcSMark Murray  * 4. Neither the name of the University nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #define	FTP_NAMES
35b528cefcSMark Murray #include "ftpd_locl.h"
36b528cefcSMark Murray #ifdef KRB5
37b528cefcSMark Murray #include <krb5.h>
38b528cefcSMark Murray #endif
39b528cefcSMark Murray #include "getarg.h"
40b528cefcSMark Murray 
41*ae771770SStanislav Sedov RCSID("$Id$");
42b528cefcSMark Murray 
43b528cefcSMark Murray static char version[] = "Version 6.00";
44b528cefcSMark Murray 
45b528cefcSMark Murray extern	off_t restart_point;
46b528cefcSMark Murray extern	char cbuf[];
47b528cefcSMark Murray 
48b528cefcSMark Murray struct  sockaddr_storage ctrl_addr_ss;
49b528cefcSMark Murray struct  sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss;
50b528cefcSMark Murray 
51b528cefcSMark Murray struct  sockaddr_storage data_source_ss;
52b528cefcSMark Murray struct  sockaddr *data_source = (struct sockaddr *)&data_source_ss;
53b528cefcSMark Murray 
54b528cefcSMark Murray struct  sockaddr_storage data_dest_ss;
55b528cefcSMark Murray struct  sockaddr *data_dest = (struct sockaddr *)&data_dest_ss;
56b528cefcSMark Murray 
57b528cefcSMark Murray struct  sockaddr_storage his_addr_ss;
58b528cefcSMark Murray struct  sockaddr *his_addr = (struct sockaddr *)&his_addr_ss;
59b528cefcSMark Murray 
60b528cefcSMark Murray struct  sockaddr_storage pasv_addr_ss;
61b528cefcSMark Murray struct  sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss;
62b528cefcSMark Murray 
63b528cefcSMark Murray int	data;
64b528cefcSMark Murray int	logged_in;
65b528cefcSMark Murray struct	passwd *pw;
66b528cefcSMark Murray int	debug = 0;
67b528cefcSMark Murray int	ftpd_timeout = 900;    /* timeout after 15 minutes of inactivity */
68b528cefcSMark Murray int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
694137ff4cSJacques Vidrine int	restricted_data_ports = 1;
70b528cefcSMark Murray int	logging;
71b528cefcSMark Murray int	guest;
72b528cefcSMark Murray int	dochroot;
73b528cefcSMark Murray int	type;
74b528cefcSMark Murray int	form;
75b528cefcSMark Murray int	stru;			/* avoid C keyword */
76b528cefcSMark Murray int	mode;
77b528cefcSMark Murray int	usedefault = 1;		/* for data transfers */
78b528cefcSMark Murray int	pdata = -1;		/* for passive mode */
798d4ba808SJacques Vidrine int	allow_insecure_oob = 1;
808d4ba808SJacques Vidrine static int transflag;
818d4ba808SJacques Vidrine static int urgflag;
82b528cefcSMark Murray off_t	file_size;
83b528cefcSMark Murray off_t	byte_count;
84b528cefcSMark Murray #if !defined(CMASK) || CMASK == 0
85b528cefcSMark Murray #undef CMASK
86b528cefcSMark Murray #define CMASK 027
87b528cefcSMark Murray #endif
88b528cefcSMark Murray int	defumask = CMASK;		/* default umask value */
89b528cefcSMark Murray int	guest_umask = 0777;	/* Paranoia for anonymous users */
90b528cefcSMark Murray char	tmpline[10240];
91b528cefcSMark Murray char	hostname[MaxHostNameLen];
92b528cefcSMark Murray char	remotehost[MaxHostNameLen];
93b528cefcSMark Murray static char ttyline[20];
94*ae771770SStanislav Sedov int     paranoid = 1;
95b528cefcSMark Murray 
96b528cefcSMark Murray #define AUTH_PLAIN	(1 << 0) /* allow sending passwords */
97b528cefcSMark Murray #define AUTH_OTP	(1 << 1) /* passwords are one-time */
98b528cefcSMark Murray #define AUTH_FTP	(1 << 2) /* allow anonymous login */
99b528cefcSMark Murray 
100b528cefcSMark Murray static int auth_level = 0; /* Only allow kerberos login by default */
101b528cefcSMark Murray 
102b528cefcSMark Murray /*
103b528cefcSMark Murray  * Timeout intervals for retrying connections
104b528cefcSMark Murray  * to hosts that don't accept PORT cmds.  This
105b528cefcSMark Murray  * is a kludge, but given the problems with TCP...
106b528cefcSMark Murray  */
107b528cefcSMark Murray #define	SWAITMAX	90	/* wait at most 90 seconds */
108b528cefcSMark Murray #define	SWAITINT	5	/* interval between retries */
109b528cefcSMark Murray 
110b528cefcSMark Murray int	swaitmax = SWAITMAX;
111b528cefcSMark Murray int	swaitint = SWAITINT;
112b528cefcSMark Murray 
113b528cefcSMark Murray #ifdef HAVE_SETPROCTITLE
114b528cefcSMark Murray char	proctitle[BUFSIZ];	/* initial part of title */
115b528cefcSMark Murray #endif /* HAVE_SETPROCTITLE */
116b528cefcSMark Murray 
117b528cefcSMark Murray #define LOGCMD(cmd, file) \
118b528cefcSMark Murray 	if (logging > 1) \
119b528cefcSMark Murray 	    syslog(LOG_INFO,"%s %s%s", cmd, \
120b528cefcSMark Murray 		*(file) == '/' ? "" : curdir(), file);
121b528cefcSMark Murray #define LOGCMD2(cmd, file1, file2) \
122b528cefcSMark Murray 	 if (logging > 1) \
123b528cefcSMark Murray 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
124b528cefcSMark Murray 		*(file1) == '/' ? "" : curdir(), file1, \
125b528cefcSMark Murray 		*(file2) == '/' ? "" : curdir(), file2);
126b528cefcSMark Murray #define LOGBYTES(cmd, file, cnt) \
127b528cefcSMark Murray 	if (logging > 1) { \
128b528cefcSMark Murray 		if (cnt == (off_t)-1) \
129b528cefcSMark Murray 		    syslog(LOG_INFO,"%s %s%s", cmd, \
130b528cefcSMark Murray 			*(file) == '/' ? "" : curdir(), file); \
131b528cefcSMark Murray 		else \
132b528cefcSMark Murray 		    syslog(LOG_INFO, "%s %s%s = %ld bytes", \
133b528cefcSMark Murray 			cmd, (*(file) == '/') ? "" : curdir(), file, (long)cnt); \
134b528cefcSMark Murray 	}
135b528cefcSMark Murray 
136b528cefcSMark Murray static void	 ack (char *);
137b528cefcSMark Murray static void	 myoob (int);
1388d4ba808SJacques Vidrine static int	 handleoobcmd(void);
139b528cefcSMark Murray static int	 checkuser (char *, char *);
140b528cefcSMark Murray static int	 checkaccess (char *);
141b528cefcSMark Murray static FILE	*dataconn (const char *, off_t, const char *);
142c19800e8SDoug Rabson static void	 dolog (struct sockaddr *, int);
143b528cefcSMark Murray static void	 end_login (void);
144c19800e8SDoug Rabson static FILE	*getdatasock (const char *, int);
145b528cefcSMark Murray static char	*gunique (char *);
146b528cefcSMark Murray static RETSIGTYPE	 lostconn (int);
147b528cefcSMark Murray static int	 receive_data (FILE *, FILE *);
148b528cefcSMark Murray static void	 send_data (FILE *, FILE *);
149b528cefcSMark Murray static struct passwd * sgetpwnam (char *);
150b528cefcSMark Murray 
151b528cefcSMark Murray static char *
curdir(void)152b528cefcSMark Murray curdir(void)
153b528cefcSMark Murray {
154b528cefcSMark Murray 	static char path[MaxPathLen+1];	/* path + '/' + '\0' */
155b528cefcSMark Murray 
156b528cefcSMark Murray 	if (getcwd(path, sizeof(path)-1) == NULL)
157b528cefcSMark Murray 		return ("");
158b528cefcSMark Murray 	if (path[1] != '\0')		/* special case for root dir. */
159b528cefcSMark Murray 		strlcat(path, "/", sizeof(path));
160b528cefcSMark Murray 	/* For guest account, skip / since it's chrooted */
161b528cefcSMark Murray 	return (guest ? path+1 : path);
162b528cefcSMark Murray }
163b528cefcSMark Murray 
164b528cefcSMark Murray #ifndef LINE_MAX
165b528cefcSMark Murray #define LINE_MAX 1024
166b528cefcSMark Murray #endif
167b528cefcSMark Murray 
168b528cefcSMark Murray static int
parse_auth_level(char * str)169b528cefcSMark Murray parse_auth_level(char *str)
170b528cefcSMark Murray {
171b528cefcSMark Murray     char *p;
172b528cefcSMark Murray     int ret = 0;
173b528cefcSMark Murray     char *foo = NULL;
174b528cefcSMark Murray 
175b528cefcSMark Murray     for(p = strtok_r(str, ",", &foo);
176b528cefcSMark Murray 	p;
177b528cefcSMark Murray 	p = strtok_r(NULL, ",", &foo)) {
178b528cefcSMark Murray 	if(strcmp(p, "user") == 0)
179b528cefcSMark Murray 	    ;
180b528cefcSMark Murray #ifdef OTP
181b528cefcSMark Murray 	else if(strcmp(p, "otp") == 0)
182b528cefcSMark Murray 	    ret |= AUTH_PLAIN|AUTH_OTP;
183b528cefcSMark Murray #endif
184b528cefcSMark Murray 	else if(strcmp(p, "ftp") == 0 ||
185b528cefcSMark Murray 		strcmp(p, "safe") == 0)
186b528cefcSMark Murray 	    ret |= AUTH_FTP;
187b528cefcSMark Murray 	else if(strcmp(p, "plain") == 0)
188b528cefcSMark Murray 	    ret |= AUTH_PLAIN;
189b528cefcSMark Murray 	else if(strcmp(p, "none") == 0)
190b528cefcSMark Murray 	    ret |= AUTH_PLAIN|AUTH_FTP;
191b528cefcSMark Murray 	else
192b528cefcSMark Murray 	    warnx("bad value for -a: `%s'", p);
193b528cefcSMark Murray     }
194b528cefcSMark Murray     return ret;
195b528cefcSMark Murray }
196b528cefcSMark Murray 
197b528cefcSMark Murray /*
198b528cefcSMark Murray  * Print usage and die.
199b528cefcSMark Murray  */
200b528cefcSMark Murray 
201b528cefcSMark Murray static int interactive_flag;
202b528cefcSMark Murray static char *guest_umask_string;
203b528cefcSMark Murray static char *port_string;
204b528cefcSMark Murray static char *umask_string;
205b528cefcSMark Murray static char *auth_string;
206b528cefcSMark Murray 
207b528cefcSMark Murray int use_builtin_ls = -1;
208b528cefcSMark Murray 
209b528cefcSMark Murray static int help_flag;
210b528cefcSMark Murray static int version_flag;
211b528cefcSMark Murray 
2125e9cd1aeSAssar Westerlund static const char *good_chars = "+-=_,.";
2135e9cd1aeSAssar Westerlund 
214b528cefcSMark Murray struct getargs args[] = {
215b528cefcSMark Murray     { NULL, 'a', arg_string, &auth_string, "required authentication" },
216b528cefcSMark Murray     { NULL, 'i', arg_flag, &interactive_flag, "don't assume stdin is a socket" },
217b528cefcSMark Murray     { NULL, 'p', arg_string, &port_string, "what port to listen to" },
218b528cefcSMark Murray     { NULL, 'g', arg_string, &guest_umask_string, "umask for guest logins" },
219b528cefcSMark Murray     { NULL, 'l', arg_counter, &logging, "log more stuff", "" },
220b528cefcSMark Murray     { NULL, 't', arg_integer, &ftpd_timeout, "initial timeout" },
221b528cefcSMark Murray     { NULL, 'T', arg_integer, &maxtimeout, "max timeout" },
222b528cefcSMark Murray     { NULL, 'u', arg_string, &umask_string, "umask for user logins" },
2234137ff4cSJacques Vidrine     { NULL, 'U', arg_negative_flag, &restricted_data_ports, "don't use high data ports" },
2245e9cd1aeSAssar Westerlund     { NULL, 'd', arg_flag, &debug, "enable debugging" },
2255e9cd1aeSAssar Westerlund     { NULL, 'v', arg_flag, &debug, "enable debugging" },
226b528cefcSMark Murray     { "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" },
2275e9cd1aeSAssar Westerlund     { "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" },
2288d4ba808SJacques Vidrine     { "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" },
2291c43270aSJacques Vidrine #ifdef KRB5
2301c43270aSJacques Vidrine     { "gss-bindings", 0,  arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL},
2311c43270aSJacques Vidrine #endif
232b528cefcSMark Murray     { "version", 0, arg_flag, &version_flag },
233b528cefcSMark Murray     { "help", 'h', arg_flag, &help_flag }
234b528cefcSMark Murray };
235b528cefcSMark Murray 
236b528cefcSMark Murray static int num_args = sizeof(args) / sizeof(args[0]);
237b528cefcSMark Murray 
238b528cefcSMark Murray static void
usage(int code)239b528cefcSMark Murray usage (int code)
240b528cefcSMark Murray {
241b528cefcSMark Murray     arg_printusage(args, num_args, NULL, "");
242b528cefcSMark Murray     exit (code);
243b528cefcSMark Murray }
244b528cefcSMark Murray 
245b528cefcSMark Murray /* output contents of a file */
246b528cefcSMark Murray static int
show_file(const char * file,int code)247b528cefcSMark Murray show_file(const char *file, int code)
248b528cefcSMark Murray {
249b528cefcSMark Murray     FILE *f;
250b528cefcSMark Murray     char buf[128];
251b528cefcSMark Murray 
252b528cefcSMark Murray     f = fopen(file, "r");
253b528cefcSMark Murray     if(f == NULL)
254b528cefcSMark Murray 	return -1;
255b528cefcSMark Murray     while(fgets(buf, sizeof(buf), f)){
256b528cefcSMark Murray 	buf[strcspn(buf, "\r\n")] = '\0';
257b528cefcSMark Murray 	lreply(code, "%s", buf);
258b528cefcSMark Murray     }
259b528cefcSMark Murray     fclose(f);
260b528cefcSMark Murray     return 0;
261b528cefcSMark Murray }
262b528cefcSMark Murray 
263b528cefcSMark Murray int
main(int argc,char ** argv)264b528cefcSMark Murray main(int argc, char **argv)
265b528cefcSMark Murray {
2665e9cd1aeSAssar Westerlund     socklen_t his_addr_len, ctrl_addr_len;
2675e9cd1aeSAssar Westerlund     int on = 1;
268b528cefcSMark Murray     int port;
269b528cefcSMark Murray     struct servent *sp;
270b528cefcSMark Murray 
271b528cefcSMark Murray     int optind = 0;
272b528cefcSMark Murray 
273adb0ddaeSAssar Westerlund     setprogname (argv[0]);
274b528cefcSMark Murray 
275b528cefcSMark Murray     if(getarg(args, num_args, argc, argv, &optind))
276b528cefcSMark Murray 	usage(1);
277b528cefcSMark Murray 
278b528cefcSMark Murray     if(help_flag)
279b528cefcSMark Murray 	usage(0);
280b528cefcSMark Murray 
281b528cefcSMark Murray     if(version_flag) {
282b528cefcSMark Murray 	print_version(NULL);
283b528cefcSMark Murray 	exit(0);
284b528cefcSMark Murray     }
285b528cefcSMark Murray 
286b528cefcSMark Murray     if(auth_string)
287b528cefcSMark Murray 	auth_level = parse_auth_level(auth_string);
288b528cefcSMark Murray     {
289b528cefcSMark Murray 	char *p;
290b528cefcSMark Murray 	long val = 0;
291b528cefcSMark Murray 
292b528cefcSMark Murray 	if(guest_umask_string) {
293b528cefcSMark Murray 	    val = strtol(guest_umask_string, &p, 8);
294b528cefcSMark Murray 	    if (*p != '\0' || val < 0)
295b528cefcSMark Murray 		warnx("bad value for -g");
296b528cefcSMark Murray 	    else
297b528cefcSMark Murray 		guest_umask = val;
298b528cefcSMark Murray 	}
299b528cefcSMark Murray 	if(umask_string) {
300b528cefcSMark Murray 	    val = strtol(umask_string, &p, 8);
301b528cefcSMark Murray 	    if (*p != '\0' || val < 0)
302b528cefcSMark Murray 		warnx("bad value for -u");
303b528cefcSMark Murray 	    else
304b528cefcSMark Murray 		defumask = val;
305b528cefcSMark Murray 	}
306b528cefcSMark Murray     }
3078373020dSJacques Vidrine     sp = getservbyname("ftp", "tcp");
3088373020dSJacques Vidrine     if(sp)
3098373020dSJacques Vidrine 	port = sp->s_port;
3108373020dSJacques Vidrine     else
3118373020dSJacques Vidrine 	port = htons(21);
312b528cefcSMark Murray     if(port_string) {
313b528cefcSMark Murray 	sp = getservbyname(port_string, "tcp");
314b528cefcSMark Murray 	if(sp)
315b528cefcSMark Murray 	    port = sp->s_port;
316b528cefcSMark Murray 	else
317bbd80c28SJacques Vidrine 	    if(isdigit((unsigned char)port_string[0]))
318b528cefcSMark Murray 		port = htons(atoi(port_string));
319b528cefcSMark Murray 	    else
320b528cefcSMark Murray 		warnx("bad value for -p");
321b528cefcSMark Murray     }
322b528cefcSMark Murray 
323b528cefcSMark Murray     if (maxtimeout < ftpd_timeout)
324b528cefcSMark Murray 	maxtimeout = ftpd_timeout;
325b528cefcSMark Murray 
326b528cefcSMark Murray #if 0
327b528cefcSMark Murray     if (ftpd_timeout > maxtimeout)
328b528cefcSMark Murray 	ftpd_timeout = maxtimeout;
329b528cefcSMark Murray #endif
330b528cefcSMark Murray 
331b528cefcSMark Murray     if(interactive_flag)
332*ae771770SStanislav Sedov 	mini_inetd(port, NULL);
333b528cefcSMark Murray 
334b528cefcSMark Murray     /*
335b528cefcSMark Murray      * LOG_NDELAY sets up the logging connection immediately,
336b528cefcSMark Murray      * necessary for anonymous ftp's that chroot and can't do it later.
337b528cefcSMark Murray      */
338b528cefcSMark Murray     openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
339b528cefcSMark Murray     his_addr_len = sizeof(his_addr_ss);
340b528cefcSMark Murray     if (getpeername(STDIN_FILENO, his_addr, &his_addr_len) < 0) {
341b528cefcSMark Murray 	syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
342b528cefcSMark Murray 	exit(1);
343b528cefcSMark Murray     }
344b528cefcSMark Murray     ctrl_addr_len = sizeof(ctrl_addr_ss);
345b528cefcSMark Murray     if (getsockname(STDIN_FILENO, ctrl_addr, &ctrl_addr_len) < 0) {
346b528cefcSMark Murray 	syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
347b528cefcSMark Murray 	exit(1);
348b528cefcSMark Murray     }
349*ae771770SStanislav Sedov #if defined(IP_TOS)
350*ae771770SStanislav Sedov     if (ctrl_addr->sa_family == AF_INET)
351*ae771770SStanislav Sedov 	socket_set_tos(STDIN_FILENO, IP_TOS);
352b528cefcSMark Murray #endif
353b528cefcSMark Murray     data_source->sa_family = ctrl_addr->sa_family;
354b528cefcSMark Murray     socket_set_port (data_source,
355b528cefcSMark Murray 		     htons(ntohs(socket_get_port(ctrl_addr)) - 1));
356b528cefcSMark Murray 
357b528cefcSMark Murray     /* set this here so it can be put in wtmp */
358b528cefcSMark Murray     snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid());
359b528cefcSMark Murray 
360b528cefcSMark Murray 
361b528cefcSMark Murray     /*	freopen(_PATH_DEVNULL, "w", stderr); */
362b528cefcSMark Murray     signal(SIGPIPE, lostconn);
363b528cefcSMark Murray     signal(SIGCHLD, SIG_IGN);
364b528cefcSMark Murray #ifdef SIGURG
365b528cefcSMark Murray     if (signal(SIGURG, myoob) == SIG_ERR)
366b528cefcSMark Murray 	syslog(LOG_ERR, "signal: %m");
367b528cefcSMark Murray #endif
368b528cefcSMark Murray 
369b528cefcSMark Murray     /* Try to handle urgent data inline */
370b528cefcSMark Murray #if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT)
371b528cefcSMark Murray     if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (void *)&on,
372b528cefcSMark Murray 		   sizeof(on)) < 0)
373b528cefcSMark Murray 	syslog(LOG_ERR, "setsockopt: %m");
374b528cefcSMark Murray #endif
375b528cefcSMark Murray 
376b528cefcSMark Murray #ifdef	F_SETOWN
377b528cefcSMark Murray     if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
378b528cefcSMark Murray 	syslog(LOG_ERR, "fcntl F_SETOWN: %m");
379b528cefcSMark Murray #endif
380b528cefcSMark Murray     dolog(his_addr, his_addr_len);
381b528cefcSMark Murray     /*
382b528cefcSMark Murray      * Set up default state
383b528cefcSMark Murray      */
384b528cefcSMark Murray     data = -1;
385b528cefcSMark Murray     type = TYPE_A;
386b528cefcSMark Murray     form = FORM_N;
387b528cefcSMark Murray     stru = STRU_F;
388b528cefcSMark Murray     mode = MODE_S;
389b528cefcSMark Murray     tmpline[0] = '\0';
390b528cefcSMark Murray 
391b528cefcSMark Murray     /* If logins are disabled, print out the message. */
392b528cefcSMark Murray     if(show_file(_PATH_NOLOGIN, 530) == 0) {
393b528cefcSMark Murray 	reply(530, "System not available.");
394b528cefcSMark Murray 	exit(0);
395b528cefcSMark Murray     }
396b528cefcSMark Murray     show_file(_PATH_FTPWELCOME, 220);
397b528cefcSMark Murray     /* reply(220,) must follow */
398b528cefcSMark Murray     gethostname(hostname, sizeof(hostname));
399b528cefcSMark Murray 
400b528cefcSMark Murray     reply(220, "%s FTP server (%s"
401b528cefcSMark Murray #ifdef KRB5
402b528cefcSMark Murray 	  "+%s"
403b528cefcSMark Murray #endif
404b528cefcSMark Murray 	  ") ready.", hostname, version
405b528cefcSMark Murray #ifdef KRB5
406b528cefcSMark Murray 	  ,heimdal_version
407b528cefcSMark Murray #endif
408b528cefcSMark Murray 	  );
409b528cefcSMark Murray 
410b528cefcSMark Murray     for (;;)
411b528cefcSMark Murray 	yyparse();
412b528cefcSMark Murray     /* NOTREACHED */
413b528cefcSMark Murray }
414b528cefcSMark Murray 
415b528cefcSMark Murray static RETSIGTYPE
lostconn(int signo)416b528cefcSMark Murray lostconn(int signo)
417b528cefcSMark Murray {
418b528cefcSMark Murray 
419b528cefcSMark Murray 	if (debug)
420b528cefcSMark Murray 		syslog(LOG_DEBUG, "lost connection");
421b528cefcSMark Murray 	dologout(-1);
422b528cefcSMark Murray }
423b528cefcSMark Murray 
424b528cefcSMark Murray /*
425b528cefcSMark Murray  * Helper function for sgetpwnam().
426b528cefcSMark Murray  */
427b528cefcSMark Murray static char *
sgetsave(char * s)428b528cefcSMark Murray sgetsave(char *s)
429b528cefcSMark Murray {
430b528cefcSMark Murray 	char *new = strdup(s);
431b528cefcSMark Murray 
432b528cefcSMark Murray 	if (new == NULL) {
433b528cefcSMark Murray 		perror_reply(421, "Local resource failure: malloc");
434b528cefcSMark Murray 		dologout(1);
435b528cefcSMark Murray 		/* NOTREACHED */
436b528cefcSMark Murray 	}
437b528cefcSMark Murray 	return new;
438b528cefcSMark Murray }
439b528cefcSMark Murray 
440b528cefcSMark Murray /*
441b528cefcSMark Murray  * Save the result of a getpwnam.  Used for USER command, since
442b528cefcSMark Murray  * the data returned must not be clobbered by any other command
443b528cefcSMark Murray  * (e.g., globbing).
444b528cefcSMark Murray  */
445b528cefcSMark Murray static struct passwd *
sgetpwnam(char * name)446b528cefcSMark Murray sgetpwnam(char *name)
447b528cefcSMark Murray {
448b528cefcSMark Murray 	static struct passwd save;
449b528cefcSMark Murray 	struct passwd *p;
450b528cefcSMark Murray 
451b528cefcSMark Murray 	if ((p = k_getpwnam(name)) == NULL)
452b528cefcSMark Murray 		return (p);
453b528cefcSMark Murray 	if (save.pw_name) {
454b528cefcSMark Murray 		free(save.pw_name);
455b528cefcSMark Murray 		free(save.pw_passwd);
456b528cefcSMark Murray 		free(save.pw_gecos);
457b528cefcSMark Murray 		free(save.pw_dir);
458b528cefcSMark Murray 		free(save.pw_shell);
459b528cefcSMark Murray 	}
460b528cefcSMark Murray 	save = *p;
461b528cefcSMark Murray 	save.pw_name = sgetsave(p->pw_name);
462b528cefcSMark Murray 	save.pw_passwd = sgetsave(p->pw_passwd);
463b528cefcSMark Murray 	save.pw_gecos = sgetsave(p->pw_gecos);
464b528cefcSMark Murray 	save.pw_dir = sgetsave(p->pw_dir);
465b528cefcSMark Murray 	save.pw_shell = sgetsave(p->pw_shell);
466b528cefcSMark Murray 	return (&save);
467b528cefcSMark Murray }
468b528cefcSMark Murray 
469b528cefcSMark Murray static int login_attempts;	/* number of failed login attempts */
470b528cefcSMark Murray static int askpasswd;		/* had user command, ask for passwd */
471b528cefcSMark Murray static char curname[10];	/* current USER name */
472b528cefcSMark Murray #ifdef OTP
473b528cefcSMark Murray OtpContext otp_ctx;
474b528cefcSMark Murray #endif
475b528cefcSMark Murray 
476b528cefcSMark Murray /*
477b528cefcSMark Murray  * USER command.
478b528cefcSMark Murray  * Sets global passwd pointer pw if named account exists and is acceptable;
479b528cefcSMark Murray  * sets askpasswd if a PASS command is expected.  If logged in previously,
480b528cefcSMark Murray  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
481b528cefcSMark Murray  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
482b528cefcSMark Murray  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
483b528cefcSMark Murray  * requesting login privileges.  Disallow anyone who does not have a standard
484b528cefcSMark Murray  * shell as returned by getusershell().  Disallow anyone mentioned in the file
485b528cefcSMark Murray  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
486b528cefcSMark Murray  */
487b528cefcSMark Murray void
user(char * name)488b528cefcSMark Murray user(char *name)
489b528cefcSMark Murray {
490b528cefcSMark Murray 	char *cp, *shell;
491b528cefcSMark Murray 
492b528cefcSMark Murray 	if(auth_level == 0 && !sec_complete){
493b528cefcSMark Murray 	    reply(530, "No login allowed without authorization.");
494b528cefcSMark Murray 	    return;
495b528cefcSMark Murray 	}
496b528cefcSMark Murray 
497b528cefcSMark Murray 	if (logged_in) {
498b528cefcSMark Murray 		if (guest) {
499b528cefcSMark Murray 			reply(530, "Can't change user from guest login.");
500b528cefcSMark Murray 			return;
501b528cefcSMark Murray 		} else if (dochroot) {
502b528cefcSMark Murray 			reply(530, "Can't change user from chroot user.");
503b528cefcSMark Murray 			return;
504b528cefcSMark Murray 		}
505b528cefcSMark Murray 		end_login();
506b528cefcSMark Murray 	}
507b528cefcSMark Murray 
508b528cefcSMark Murray 	guest = 0;
509b528cefcSMark Murray 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
510b528cefcSMark Murray 	    if ((auth_level & AUTH_FTP) == 0 ||
511b528cefcSMark Murray 		checkaccess("ftp") ||
512b528cefcSMark Murray 		checkaccess("anonymous"))
513b528cefcSMark Murray 		reply(530, "User %s access denied.", name);
514b528cefcSMark Murray 	    else if ((pw = sgetpwnam("ftp")) != NULL) {
515b528cefcSMark Murray 		guest = 1;
516b528cefcSMark Murray 		defumask = guest_umask;	/* paranoia for incoming */
517b528cefcSMark Murray 		askpasswd = 1;
518b528cefcSMark Murray 		reply(331, "Guest login ok, type your name as password.");
519b528cefcSMark Murray 	    } else
520b528cefcSMark Murray 		reply(530, "User %s unknown.", name);
521b528cefcSMark Murray 	    if (!askpasswd && logging) {
522b528cefcSMark Murray 		char data_addr[256];
523b528cefcSMark Murray 
524b528cefcSMark Murray 		if (inet_ntop (his_addr->sa_family,
525b528cefcSMark Murray 			       socket_get_address(his_addr),
526b528cefcSMark Murray 			       data_addr, sizeof(data_addr)) == NULL)
527b528cefcSMark Murray 			strlcpy (data_addr, "unknown address",
528b528cefcSMark Murray 					 sizeof(data_addr));
529b528cefcSMark Murray 
530b528cefcSMark Murray 		syslog(LOG_NOTICE,
531b528cefcSMark Murray 		       "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)",
532b528cefcSMark Murray 		       remotehost, data_addr);
533b528cefcSMark Murray 	    }
534b528cefcSMark Murray 	    return;
535b528cefcSMark Murray 	}
536b528cefcSMark Murray 	if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){
537b528cefcSMark Murray 	    reply(530, "Only authorized and anonymous login allowed.");
538b528cefcSMark Murray 	    return;
539b528cefcSMark Murray 	}
540b528cefcSMark Murray 	if ((pw = sgetpwnam(name))) {
541b528cefcSMark Murray 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
542b528cefcSMark Murray 			shell = _PATH_BSHELL;
543b528cefcSMark Murray 		while ((cp = getusershell()) != NULL)
544b528cefcSMark Murray 			if (strcmp(cp, shell) == 0)
545b528cefcSMark Murray 				break;
546b528cefcSMark Murray 		endusershell();
547b528cefcSMark Murray 
548b528cefcSMark Murray 		if (cp == NULL || checkaccess(name)) {
549b528cefcSMark Murray 			reply(530, "User %s access denied.", name);
550b528cefcSMark Murray 			if (logging) {
551b528cefcSMark Murray 				char data_addr[256];
552b528cefcSMark Murray 
553b528cefcSMark Murray 				if (inet_ntop (his_addr->sa_family,
554b528cefcSMark Murray 					       socket_get_address(his_addr),
555b528cefcSMark Murray 					       data_addr,
556b528cefcSMark Murray 					       sizeof(data_addr)) == NULL)
557b528cefcSMark Murray 					strlcpy (data_addr,
558b528cefcSMark Murray 							 "unknown address",
559b528cefcSMark Murray 							 sizeof(data_addr));
560b528cefcSMark Murray 
561b528cefcSMark Murray 				syslog(LOG_NOTICE,
562b528cefcSMark Murray 				       "FTP LOGIN REFUSED FROM %s(%s), %s",
563b528cefcSMark Murray 				       remotehost,
564b528cefcSMark Murray 				       data_addr,
565b528cefcSMark Murray 				       name);
566b528cefcSMark Murray 			}
567b528cefcSMark Murray 			pw = (struct passwd *) NULL;
568b528cefcSMark Murray 			return;
569b528cefcSMark Murray 		}
570b528cefcSMark Murray 	}
571b528cefcSMark Murray 	if (logging)
572b528cefcSMark Murray 	    strlcpy(curname, name, sizeof(curname));
573b528cefcSMark Murray 	if(sec_complete) {
574c19800e8SDoug Rabson 	    if(sec_userok(name) == 0) {
575b528cefcSMark Murray 		do_login(232, name);
576c19800e8SDoug Rabson 		sec_session(name);
577c19800e8SDoug Rabson 	    } else
578b528cefcSMark Murray 		reply(530, "User %s access denied.", name);
579b528cefcSMark Murray 	} else {
580c19800e8SDoug Rabson #ifdef OTP
581b528cefcSMark Murray 		char ss[256];
582b528cefcSMark Murray 
583b528cefcSMark Murray 		if (otp_challenge(&otp_ctx, name, ss, sizeof(ss)) == 0) {
584b528cefcSMark Murray 			reply(331, "Password %s for %s required.",
585b528cefcSMark Murray 			      ss, name);
586b528cefcSMark Murray 			askpasswd = 1;
587b528cefcSMark Murray 		} else
588b528cefcSMark Murray #endif
589b528cefcSMark Murray 		if ((auth_level & AUTH_OTP) == 0) {
590b528cefcSMark Murray 		    reply(331, "Password required for %s.", name);
591b528cefcSMark Murray 		    askpasswd = 1;
592b528cefcSMark Murray 		} else {
593c19800e8SDoug Rabson #ifdef OTP
594b528cefcSMark Murray 		    char *s;
595b528cefcSMark Murray 
596b528cefcSMark Murray 		    if ((s = otp_error (&otp_ctx)) != NULL)
597b528cefcSMark Murray 			lreply(530, "OTP: %s", s);
598b528cefcSMark Murray #endif
599b528cefcSMark Murray 		    reply(530,
600b528cefcSMark Murray 			  "Only authorized, anonymous"
601b528cefcSMark Murray #ifdef OTP
602b528cefcSMark Murray 			  " and OTP "
603b528cefcSMark Murray #endif
604b528cefcSMark Murray 			  "login allowed.");
605b528cefcSMark Murray 		}
606b528cefcSMark Murray 
607b528cefcSMark Murray 	}
608b528cefcSMark Murray 	/*
609b528cefcSMark Murray 	 * Delay before reading passwd after first failed
610b528cefcSMark Murray 	 * attempt to slow down passwd-guessing programs.
611b528cefcSMark Murray 	 */
612b528cefcSMark Murray 	if (login_attempts)
613b528cefcSMark Murray 		sleep(login_attempts);
614b528cefcSMark Murray }
615b528cefcSMark Murray 
616b528cefcSMark Murray /*
617b528cefcSMark Murray  * Check if a user is in the file "fname"
618b528cefcSMark Murray  */
619b528cefcSMark Murray static int
checkuser(char * fname,char * name)620b528cefcSMark Murray checkuser(char *fname, char *name)
621b528cefcSMark Murray {
622b528cefcSMark Murray 	FILE *fd;
623b528cefcSMark Murray 	int found = 0;
624b528cefcSMark Murray 	char *p, line[BUFSIZ];
625b528cefcSMark Murray 
626b528cefcSMark Murray 	if ((fd = fopen(fname, "r")) != NULL) {
627b528cefcSMark Murray 		while (fgets(line, sizeof(line), fd) != NULL)
628b528cefcSMark Murray 			if ((p = strchr(line, '\n')) != NULL) {
629b528cefcSMark Murray 				*p = '\0';
630b528cefcSMark Murray 				if (line[0] == '#')
631b528cefcSMark Murray 					continue;
632b528cefcSMark Murray 				if (strcmp(line, name) == 0) {
633b528cefcSMark Murray 					found = 1;
634b528cefcSMark Murray 					break;
635b528cefcSMark Murray 				}
636b528cefcSMark Murray 			}
637b528cefcSMark Murray 		fclose(fd);
638b528cefcSMark Murray 	}
639b528cefcSMark Murray 	return (found);
640b528cefcSMark Murray }
641b528cefcSMark Murray 
642b528cefcSMark Murray 
643b528cefcSMark Murray /*
644b528cefcSMark Murray  * Determine whether a user has access, based on information in
645b528cefcSMark Murray  * _PATH_FTPUSERS. The users are listed one per line, with `allow'
646b528cefcSMark Murray  * or `deny' after the username. If anything other than `allow', or
647b528cefcSMark Murray  * just nothing, is given after the username, `deny' is assumed.
648b528cefcSMark Murray  *
649b528cefcSMark Murray  * If the user is not found in the file, but the pseudo-user `*' is,
650b528cefcSMark Murray  * the permission is taken from that line.
651b528cefcSMark Murray  *
652b528cefcSMark Murray  * This preserves the old semantics where if a user was listed in the
653b528cefcSMark Murray  * file he was denied, otherwise he was allowed.
654b528cefcSMark Murray  *
655b528cefcSMark Murray  * Return 1 if the user is denied, or 0 if he is allowed.  */
656b528cefcSMark Murray 
657b528cefcSMark Murray static int
match(const char * pattern,const char * string)658b528cefcSMark Murray match(const char *pattern, const char *string)
659b528cefcSMark Murray {
660b528cefcSMark Murray     return fnmatch(pattern, string, FNM_NOESCAPE);
661b528cefcSMark Murray }
662b528cefcSMark Murray 
663b528cefcSMark Murray static int
checkaccess(char * name)664b528cefcSMark Murray checkaccess(char *name)
665b528cefcSMark Murray {
666b528cefcSMark Murray #define ALLOWED		0
667b528cefcSMark Murray #define	NOT_ALLOWED	1
668b528cefcSMark Murray     FILE *fd;
669b528cefcSMark Murray     int allowed = ALLOWED;
670b528cefcSMark Murray     char *user, *perm, line[BUFSIZ];
671b528cefcSMark Murray     char *foo;
672b528cefcSMark Murray 
673b528cefcSMark Murray     fd = fopen(_PATH_FTPUSERS, "r");
674b528cefcSMark Murray 
675b528cefcSMark Murray     if(fd == NULL)
676b528cefcSMark Murray 	return allowed;
677b528cefcSMark Murray 
678b528cefcSMark Murray     while (fgets(line, sizeof(line), fd) != NULL)  {
679b528cefcSMark Murray 	foo = NULL;
680b528cefcSMark Murray 	user = strtok_r(line, " \t\n", &foo);
681b528cefcSMark Murray 	if (user == NULL || user[0] == '#')
682b528cefcSMark Murray 	    continue;
683b528cefcSMark Murray 	perm = strtok_r(NULL, " \t\n", &foo);
684b528cefcSMark Murray 	if (match(user, name) == 0){
685b528cefcSMark Murray 	    if(perm && strcmp(perm, "allow") == 0)
686b528cefcSMark Murray 		allowed = ALLOWED;
687b528cefcSMark Murray 	    else
688b528cefcSMark Murray 		allowed = NOT_ALLOWED;
689b528cefcSMark Murray 	    break;
690b528cefcSMark Murray 	}
691b528cefcSMark Murray     }
692b528cefcSMark Murray     fclose(fd);
693b528cefcSMark Murray     return allowed;
694b528cefcSMark Murray }
695b528cefcSMark Murray #undef	ALLOWED
696b528cefcSMark Murray #undef	NOT_ALLOWED
697b528cefcSMark Murray 
698b528cefcSMark Murray 
do_login(int code,char * passwd)699b528cefcSMark Murray int do_login(int code, char *passwd)
700b528cefcSMark Murray {
701b528cefcSMark Murray     login_attempts = 0;		/* this time successful */
702b528cefcSMark Murray     if (setegid((gid_t)pw->pw_gid) < 0) {
703b528cefcSMark Murray 	reply(550, "Can't set gid.");
704b528cefcSMark Murray 	return -1;
705b528cefcSMark Murray     }
706b528cefcSMark Murray     initgroups(pw->pw_name, pw->pw_gid);
707*ae771770SStanislav Sedov #if defined(KRB5)
708c19800e8SDoug Rabson     if(k_hasafs())
709c19800e8SDoug Rabson 	k_setpag();
710c19800e8SDoug Rabson #endif
711b528cefcSMark Murray 
712b528cefcSMark Murray     /* open wtmp before chroot */
713b528cefcSMark Murray     ftpd_logwtmp(ttyline, pw->pw_name, remotehost);
714b528cefcSMark Murray     logged_in = 1;
715b528cefcSMark Murray 
716b528cefcSMark Murray     dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
717b528cefcSMark Murray     if (guest) {
718b528cefcSMark Murray 	/*
719b528cefcSMark Murray 	 * We MUST do a chdir() after the chroot. Otherwise
720b528cefcSMark Murray 	 * the old current directory will be accessible as "."
721b528cefcSMark Murray 	 * outside the new root!
722b528cefcSMark Murray 	 */
723b528cefcSMark Murray 	if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
724b528cefcSMark Murray 	    reply(550, "Can't set guest privileges.");
725b528cefcSMark Murray 	    return -1;
726b528cefcSMark Murray 	}
727b528cefcSMark Murray     } else if (dochroot) {
728b528cefcSMark Murray 	if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
729b528cefcSMark Murray 	    reply(550, "Can't change root.");
730b528cefcSMark Murray 	    return -1;
731b528cefcSMark Murray 	}
732b528cefcSMark Murray     } else if (chdir(pw->pw_dir) < 0) {
733b528cefcSMark Murray 	if (chdir("/") < 0) {
734b528cefcSMark Murray 	    reply(530, "User %s: can't change directory to %s.",
735b528cefcSMark Murray 		  pw->pw_name, pw->pw_dir);
736b528cefcSMark Murray 	    return -1;
737b528cefcSMark Murray 	} else
738b528cefcSMark Murray 	    lreply(code, "No directory! Logging in with home=/");
739b528cefcSMark Murray     }
740b528cefcSMark Murray     if (seteuid((uid_t)pw->pw_uid) < 0) {
741b528cefcSMark Murray 	reply(550, "Can't set uid.");
742b528cefcSMark Murray 	return -1;
743b528cefcSMark Murray     }
744b528cefcSMark Murray 
745b528cefcSMark Murray     if(use_builtin_ls == -1) {
746b528cefcSMark Murray 	struct stat st;
747b528cefcSMark Murray 	/* if /bin/ls exist and is a regular file, use it, otherwise
748b528cefcSMark Murray            use built-in ls */
749b528cefcSMark Murray 	if(stat("/bin/ls", &st) == 0 &&
750b528cefcSMark Murray 	   S_ISREG(st.st_mode))
751b528cefcSMark Murray 	    use_builtin_ls = 0;
752b528cefcSMark Murray 	else
753b528cefcSMark Murray 	    use_builtin_ls = 1;
754b528cefcSMark Murray     }
755b528cefcSMark Murray 
756b528cefcSMark Murray     /*
757b528cefcSMark Murray      * Display a login message, if it exists.
758b528cefcSMark Murray      * N.B. reply(code,) must follow the message.
759b528cefcSMark Murray      */
760b528cefcSMark Murray     show_file(_PATH_FTPLOGINMESG, code);
761b528cefcSMark Murray     if(show_file(_PATH_ISSUE_NET, code) != 0)
762b528cefcSMark Murray 	show_file(_PATH_ISSUE, code);
763b528cefcSMark Murray     if (guest) {
764b528cefcSMark Murray 	reply(code, "Guest login ok, access restrictions apply.");
765b528cefcSMark Murray #ifdef HAVE_SETPROCTITLE
766b528cefcSMark Murray 	snprintf (proctitle, sizeof(proctitle),
767b528cefcSMark Murray 		  "%s: anonymous/%s",
768b528cefcSMark Murray 		  remotehost,
769b528cefcSMark Murray 		  passwd);
770b904de74SKris Kennaway 	setproctitle("%s", proctitle);
771b528cefcSMark Murray #endif /* HAVE_SETPROCTITLE */
772b528cefcSMark Murray 	if (logging) {
773b528cefcSMark Murray 	    char data_addr[256];
774b528cefcSMark Murray 
775b528cefcSMark Murray 	    if (inet_ntop (his_addr->sa_family,
776b528cefcSMark Murray 			   socket_get_address(his_addr),
777b528cefcSMark Murray 			   data_addr, sizeof(data_addr)) == NULL)
778b528cefcSMark Murray 		strlcpy (data_addr, "unknown address",
779b528cefcSMark Murray 				 sizeof(data_addr));
780b528cefcSMark Murray 
781b528cefcSMark Murray 	    syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s",
782b528cefcSMark Murray 		   remotehost,
783b528cefcSMark Murray 		   data_addr,
784b528cefcSMark Murray 		   passwd);
785b528cefcSMark Murray 	}
786b528cefcSMark Murray     } else {
787b528cefcSMark Murray 	reply(code, "User %s logged in.", pw->pw_name);
788b528cefcSMark Murray #ifdef HAVE_SETPROCTITLE
789b528cefcSMark Murray 	snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
790b904de74SKris Kennaway 	setproctitle("%s", proctitle);
791b528cefcSMark Murray #endif /* HAVE_SETPROCTITLE */
792b528cefcSMark Murray 	if (logging) {
793b528cefcSMark Murray 	    char data_addr[256];
794b528cefcSMark Murray 
795b528cefcSMark Murray 	    if (inet_ntop (his_addr->sa_family,
796b528cefcSMark Murray 			   socket_get_address(his_addr),
797b528cefcSMark Murray 			   data_addr, sizeof(data_addr)) == NULL)
798b528cefcSMark Murray 		strlcpy (data_addr, "unknown address",
799b528cefcSMark Murray 				 sizeof(data_addr));
800b528cefcSMark Murray 
801b528cefcSMark Murray 	    syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s",
802b528cefcSMark Murray 		   remotehost,
803b528cefcSMark Murray 		   data_addr,
804b528cefcSMark Murray 		   pw->pw_name);
805b528cefcSMark Murray 	}
806b528cefcSMark Murray     }
807b528cefcSMark Murray     umask(defumask);
808b528cefcSMark Murray     return 0;
809b528cefcSMark Murray }
810b528cefcSMark Murray 
811b528cefcSMark Murray /*
812b528cefcSMark Murray  * Terminate login as previous user, if any, resetting state;
813b528cefcSMark Murray  * used when USER command is given or login fails.
814b528cefcSMark Murray  */
815b528cefcSMark Murray static void
end_login(void)816b528cefcSMark Murray end_login(void)
817b528cefcSMark Murray {
818b528cefcSMark Murray 
819c19800e8SDoug Rabson 	if (seteuid((uid_t)0) < 0)
820c19800e8SDoug Rabson 		fatal("Failed to seteuid");
821b528cefcSMark Murray 	if (logged_in)
822b528cefcSMark Murray 		ftpd_logwtmp(ttyline, "", "");
823b528cefcSMark Murray 	pw = NULL;
824b528cefcSMark Murray 	logged_in = 0;
825b528cefcSMark Murray 	guest = 0;
826b528cefcSMark Murray 	dochroot = 0;
827b528cefcSMark Murray }
828b528cefcSMark Murray 
8295e9cd1aeSAssar Westerlund #ifdef KRB5
8305e9cd1aeSAssar Westerlund static int
krb5_verify(struct passwd * pwd,char * passwd)8315e9cd1aeSAssar Westerlund krb5_verify(struct passwd *pwd, char *passwd)
8325e9cd1aeSAssar Westerlund {
8335e9cd1aeSAssar Westerlund    krb5_context context;
8345e9cd1aeSAssar Westerlund    krb5_ccache  id;
8355e9cd1aeSAssar Westerlund    krb5_principal princ;
8365e9cd1aeSAssar Westerlund    krb5_error_code ret;
8375e9cd1aeSAssar Westerlund 
8385e9cd1aeSAssar Westerlund    ret = krb5_init_context(&context);
8395e9cd1aeSAssar Westerlund    if(ret)
8405e9cd1aeSAssar Westerlund         return ret;
8415e9cd1aeSAssar Westerlund 
8425e9cd1aeSAssar Westerlund   ret = krb5_parse_name(context, pwd->pw_name, &princ);
8435e9cd1aeSAssar Westerlund   if(ret){
8445e9cd1aeSAssar Westerlund         krb5_free_context(context);
8455e9cd1aeSAssar Westerlund         return ret;
8465e9cd1aeSAssar Westerlund   }
847*ae771770SStanislav Sedov   ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);
8485e9cd1aeSAssar Westerlund   if(ret){
8495e9cd1aeSAssar Westerlund         krb5_free_principal(context, princ);
8505e9cd1aeSAssar Westerlund         krb5_free_context(context);
8515e9cd1aeSAssar Westerlund         return ret;
8525e9cd1aeSAssar Westerlund   }
8535e9cd1aeSAssar Westerlund   ret = krb5_verify_user(context,
8545e9cd1aeSAssar Westerlund                          princ,
8555e9cd1aeSAssar Westerlund                          id,
8565e9cd1aeSAssar Westerlund                          passwd,
8575e9cd1aeSAssar Westerlund                          1,
8585e9cd1aeSAssar Westerlund                          NULL);
8595e9cd1aeSAssar Westerlund   krb5_free_principal(context, princ);
8605e9cd1aeSAssar Westerlund   if (k_hasafs()) {
8615e9cd1aeSAssar Westerlund       krb5_afslog_uid_home(context, id,NULL, NULL,pwd->pw_uid, pwd->pw_dir);
8625e9cd1aeSAssar Westerlund   }
8635e9cd1aeSAssar Westerlund   krb5_cc_destroy(context, id);
8645e9cd1aeSAssar Westerlund   krb5_free_context (context);
8655e9cd1aeSAssar Westerlund   if(ret)
8665e9cd1aeSAssar Westerlund       return ret;
8675e9cd1aeSAssar Westerlund   return 0;
8685e9cd1aeSAssar Westerlund }
8695e9cd1aeSAssar Westerlund #endif /* KRB5 */
8705e9cd1aeSAssar Westerlund 
871b528cefcSMark Murray void
pass(char * passwd)872b528cefcSMark Murray pass(char *passwd)
873b528cefcSMark Murray {
874b528cefcSMark Murray 	int rval;
875b528cefcSMark Murray 
876b528cefcSMark Murray 	/* some clients insists on sending a password */
877b528cefcSMark Murray 	if (logged_in && askpasswd == 0){
8785e9cd1aeSAssar Westerlund 	    reply(230, "Password not necessary");
879b528cefcSMark Murray 	    return;
880b528cefcSMark Murray 	}
881b528cefcSMark Murray 
882b528cefcSMark Murray 	if (logged_in || askpasswd == 0) {
883b528cefcSMark Murray 		reply(503, "Login with USER first.");
884b528cefcSMark Murray 		return;
885b528cefcSMark Murray 	}
886b528cefcSMark Murray 	askpasswd = 0;
887b528cefcSMark Murray 	rval = 1;
888b528cefcSMark Murray 	if (!guest) {		/* "ftp" is only account allowed no password */
889b528cefcSMark Murray 		if (pw == NULL)
890b528cefcSMark Murray 			rval = 1;	/* failure below */
891b528cefcSMark Murray #ifdef OTP
892b528cefcSMark Murray 		else if (otp_verify_user (&otp_ctx, passwd) == 0) {
893b528cefcSMark Murray 		    rval = 0;
894b528cefcSMark Murray 		}
895b528cefcSMark Murray #endif
896b528cefcSMark Murray 		else if((auth_level & AUTH_OTP) == 0) {
8975e9cd1aeSAssar Westerlund #ifdef KRB5
8985e9cd1aeSAssar Westerlund 		    rval = krb5_verify(pw, passwd);
8995e9cd1aeSAssar Westerlund #endif
9005e9cd1aeSAssar Westerlund 		    if (rval)
901b528cefcSMark Murray 			rval = unix_verify_user(pw->pw_name, passwd);
902b528cefcSMark Murray 		} else {
903b528cefcSMark Murray #ifdef OTP
904c19800e8SDoug Rabson 		    char *s;
905b528cefcSMark Murray 		    if ((s = otp_error(&otp_ctx)) != NULL)
906b528cefcSMark Murray 			lreply(530, "OTP: %s", s);
907b528cefcSMark Murray #endif
908b528cefcSMark Murray 		}
909b528cefcSMark Murray 		memset (passwd, 0, strlen(passwd));
910b528cefcSMark Murray 
911b528cefcSMark Murray 		/*
912b528cefcSMark Murray 		 * If rval == 1, the user failed the authentication
913b528cefcSMark Murray 		 * check above.  If rval == 0, either Kerberos or
914b528cefcSMark Murray 		 * local authentication succeeded.
915b528cefcSMark Murray 		 */
916b528cefcSMark Murray 		if (rval) {
917b528cefcSMark Murray 			char data_addr[256];
918b528cefcSMark Murray 
919b528cefcSMark Murray 			if (inet_ntop (his_addr->sa_family,
920b528cefcSMark Murray 				       socket_get_address(his_addr),
921b528cefcSMark Murray 				       data_addr, sizeof(data_addr)) == NULL)
922b528cefcSMark Murray 				strlcpy (data_addr, "unknown address",
923b528cefcSMark Murray 						 sizeof(data_addr));
924b528cefcSMark Murray 
925b528cefcSMark Murray 			reply(530, "Login incorrect.");
926b528cefcSMark Murray 			if (logging)
927b528cefcSMark Murray 				syslog(LOG_NOTICE,
928b528cefcSMark Murray 				    "FTP LOGIN FAILED FROM %s(%s), %s",
929b528cefcSMark Murray 				       remotehost,
930b528cefcSMark Murray 				       data_addr,
931b528cefcSMark Murray 				       curname);
932b528cefcSMark Murray 			pw = NULL;
933b528cefcSMark Murray 			if (login_attempts++ >= 5) {
934b528cefcSMark Murray 				syslog(LOG_NOTICE,
935b528cefcSMark Murray 				       "repeated login failures from %s(%s)",
936b528cefcSMark Murray 				       remotehost,
937b528cefcSMark Murray 				       data_addr);
938b528cefcSMark Murray 				exit(0);
939b528cefcSMark Murray 			}
940b528cefcSMark Murray 			return;
941b528cefcSMark Murray 		}
942b528cefcSMark Murray 	}
943b528cefcSMark Murray 	if(!do_login(230, passwd))
944b528cefcSMark Murray 	  return;
945b528cefcSMark Murray 
946b528cefcSMark Murray 	/* Forget all about it... */
947b528cefcSMark Murray 	end_login();
948b528cefcSMark Murray }
949b528cefcSMark Murray 
950b528cefcSMark Murray void
retrieve(const char * cmd,char * name)951b528cefcSMark Murray retrieve(const char *cmd, char *name)
952b528cefcSMark Murray {
953b528cefcSMark Murray 	FILE *fin = NULL, *dout;
954b528cefcSMark Murray 	struct stat st;
955b528cefcSMark Murray 	int (*closefunc) (FILE *);
956b528cefcSMark Murray 	char line[BUFSIZ];
957b528cefcSMark Murray 
958b528cefcSMark Murray 
959b528cefcSMark Murray 	if (cmd == 0) {
960b528cefcSMark Murray 		fin = fopen(name, "r");
961b528cefcSMark Murray 		closefunc = fclose;
962b528cefcSMark Murray 		st.st_size = 0;
963b528cefcSMark Murray 		if(fin == NULL){
964b528cefcSMark Murray 		    int save_errno = errno;
965b528cefcSMark Murray 		    struct cmds {
966b528cefcSMark Murray 			const char *ext;
967b528cefcSMark Murray 			const char *cmd;
968b528cefcSMark Murray 		        const char *rev_cmd;
969b528cefcSMark Murray 		    } cmds[] = {
970b528cefcSMark Murray 			{".tar", "/bin/gtar cPf - %s", NULL},
971b528cefcSMark Murray 			{".tar.gz", "/bin/gtar zcPf - %s", NULL},
972b528cefcSMark Murray 			{".tar.Z", "/bin/gtar ZcPf - %s", NULL},
973b528cefcSMark Murray 			{".gz", "/bin/gzip -c -- %s", "/bin/gzip -c -d -- %s"},
974b528cefcSMark Murray 			{".Z", "/bin/compress -c -- %s", "/bin/uncompress -c -- %s"},
975b528cefcSMark Murray 			{NULL, NULL}
976b528cefcSMark Murray 		    };
977b528cefcSMark Murray 		    struct cmds *p;
978b528cefcSMark Murray 		    for(p = cmds; p->ext; p++){
979b528cefcSMark Murray 			char *tail = name + strlen(name) - strlen(p->ext);
980b528cefcSMark Murray 			char c = *tail;
981b528cefcSMark Murray 
982b528cefcSMark Murray 			if(strcmp(tail, p->ext) == 0 &&
983b528cefcSMark Murray 			   (*tail  = 0) == 0 &&
984b528cefcSMark Murray 			   access(name, R_OK) == 0){
985b528cefcSMark Murray 			    snprintf (line, sizeof(line), p->cmd, name);
986b528cefcSMark Murray 			    *tail  = c;
987b528cefcSMark Murray 			    break;
988b528cefcSMark Murray 			}
989b528cefcSMark Murray 			*tail = c;
990b528cefcSMark Murray 			if (p->rev_cmd != NULL) {
991b528cefcSMark Murray 			    char *ext;
992c19800e8SDoug Rabson 			    int ret;
993b528cefcSMark Murray 
994c19800e8SDoug Rabson 			    ret = asprintf(&ext, "%s%s", name, p->ext);
995c19800e8SDoug Rabson 			    if (ret != -1) {
996b528cefcSMark Murray   			        if (access(ext, R_OK) == 0) {
997b528cefcSMark Murray 				    snprintf (line, sizeof(line),
998b528cefcSMark Murray 					      p->rev_cmd, ext);
999b528cefcSMark Murray 				    free(ext);
1000b528cefcSMark Murray 				    break;
1001b528cefcSMark Murray 				}
1002b528cefcSMark Murray 			        free(ext);
1003b528cefcSMark Murray 			    }
1004b528cefcSMark Murray 			}
1005b528cefcSMark Murray 
1006b528cefcSMark Murray 		    }
1007b528cefcSMark Murray 		    if(p->ext){
1008b528cefcSMark Murray 			fin = ftpd_popen(line, "r", 0, 0);
1009b528cefcSMark Murray 			closefunc = ftpd_pclose;
1010b528cefcSMark Murray 			st.st_size = -1;
1011b528cefcSMark Murray 			cmd = line;
1012b528cefcSMark Murray 		    } else
1013b528cefcSMark Murray 			errno = save_errno;
1014b528cefcSMark Murray 		}
1015b528cefcSMark Murray 	} else {
1016b528cefcSMark Murray 		snprintf(line, sizeof(line), cmd, name);
1017b528cefcSMark Murray 		name = line;
1018b528cefcSMark Murray 		fin = ftpd_popen(line, "r", 1, 0);
1019b528cefcSMark Murray 		closefunc = ftpd_pclose;
1020b528cefcSMark Murray 		st.st_size = -1;
1021b528cefcSMark Murray 	}
1022b528cefcSMark Murray 	if (fin == NULL) {
1023b528cefcSMark Murray 		if (errno != 0) {
1024b528cefcSMark Murray 			perror_reply(550, name);
1025b528cefcSMark Murray 			if (cmd == 0) {
1026b528cefcSMark Murray 				LOGCMD("get", name);
1027b528cefcSMark Murray 			}
1028b528cefcSMark Murray 		}
1029b528cefcSMark Murray 		return;
1030b528cefcSMark Murray 	}
1031b528cefcSMark Murray 	byte_count = -1;
1032b528cefcSMark Murray 	if (cmd == 0){
1033b528cefcSMark Murray 	    if(fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) {
1034b528cefcSMark Murray 		reply(550, "%s: not a plain file.", name);
1035b528cefcSMark Murray 		goto done;
1036b528cefcSMark Murray 	    }
1037b528cefcSMark Murray 	}
1038b528cefcSMark Murray 	if (restart_point) {
1039b528cefcSMark Murray 		if (type == TYPE_A) {
1040b528cefcSMark Murray 			off_t i, n;
1041b528cefcSMark Murray 			int c;
1042b528cefcSMark Murray 
1043b528cefcSMark Murray 			n = restart_point;
1044b528cefcSMark Murray 			i = 0;
1045b528cefcSMark Murray 			while (i++ < n) {
1046b528cefcSMark Murray 				if ((c=getc(fin)) == EOF) {
1047b528cefcSMark Murray 					perror_reply(550, name);
1048b528cefcSMark Murray 					goto done;
1049b528cefcSMark Murray 				}
1050b528cefcSMark Murray 				if (c == '\n')
1051b528cefcSMark Murray 					i++;
1052b528cefcSMark Murray 			}
1053b528cefcSMark Murray 		} else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1054b528cefcSMark Murray 			perror_reply(550, name);
1055b528cefcSMark Murray 			goto done;
1056b528cefcSMark Murray 		}
1057b528cefcSMark Murray 	}
1058b528cefcSMark Murray 	dout = dataconn(name, st.st_size, "w");
1059b528cefcSMark Murray 	if (dout == NULL)
1060b528cefcSMark Murray 		goto done;
1061b528cefcSMark Murray 	set_buffer_size(fileno(dout), 0);
1062b528cefcSMark Murray 	send_data(fin, dout);
1063b528cefcSMark Murray 	fclose(dout);
1064b528cefcSMark Murray 	data = -1;
1065b528cefcSMark Murray 	pdata = -1;
1066b528cefcSMark Murray done:
1067b528cefcSMark Murray 	if (cmd == 0)
1068b528cefcSMark Murray 		LOGBYTES("get", name, byte_count);
1069b528cefcSMark Murray 	(*closefunc)(fin);
1070b528cefcSMark Murray }
1071b528cefcSMark Murray 
1072b528cefcSMark Murray /* filename sanity check */
1073b528cefcSMark Murray 
1074b528cefcSMark Murray int
filename_check(char * filename)1075b528cefcSMark Murray filename_check(char *filename)
1076b528cefcSMark Murray {
1077c19800e8SDoug Rabson     char *p;
1078b528cefcSMark Murray 
1079c19800e8SDoug Rabson     p = strrchr(filename, '/');
1080b528cefcSMark Murray     if(p)
1081b528cefcSMark Murray 	filename = p + 1;
1082b528cefcSMark Murray 
1083b528cefcSMark Murray     p = filename;
1084b528cefcSMark Murray 
1085c19800e8SDoug Rabson     if(isalnum((unsigned char)*p)){
1086b528cefcSMark Murray 	p++;
1087c19800e8SDoug Rabson 	while(*p && (isalnum((unsigned char)*p) || strchr(good_chars, (unsigned char)*p)))
1088b528cefcSMark Murray 	    p++;
1089b528cefcSMark Murray 	if(*p == '\0')
1090b528cefcSMark Murray 	    return 0;
1091b528cefcSMark Murray     }
10925e9cd1aeSAssar Westerlund     lreply(553, "\"%s\" is not an acceptable filename.", filename);
1093b528cefcSMark Murray     lreply(553, "The filename must start with an alphanumeric "
1094b528cefcSMark Murray 	   "character and must only");
1095b528cefcSMark Murray     reply(553, "consist of alphanumeric characters or any of the following: %s",
1096b528cefcSMark Murray 	  good_chars);
1097b528cefcSMark Murray     return 1;
1098b528cefcSMark Murray }
1099b528cefcSMark Murray 
1100b528cefcSMark Murray void
do_store(char * name,char * mode,int unique)1101b528cefcSMark Murray do_store(char *name, char *mode, int unique)
1102b528cefcSMark Murray {
1103b528cefcSMark Murray 	FILE *fout, *din;
1104b528cefcSMark Murray 	struct stat st;
1105b528cefcSMark Murray 	int (*closefunc) (FILE *);
1106b528cefcSMark Murray 
1107b528cefcSMark Murray 	if(guest && filename_check(name))
1108b528cefcSMark Murray 	    return;
1109*ae771770SStanislav Sedov 	if (unique) {
1110*ae771770SStanislav Sedov 	    char *uname;
1111*ae771770SStanislav Sedov 	    if (stat(name, &st) == 0) {
1112*ae771770SStanislav Sedov 		if ((uname = gunique(name)) == NULL)
1113b528cefcSMark Murray 		    return;
1114*ae771770SStanislav Sedov 		name = uname;
1115*ae771770SStanislav Sedov 	    }
1116*ae771770SStanislav Sedov 	    LOGCMD(*mode == 'w' ? "put" : "append", name);
1117b528cefcSMark Murray 	}
1118b528cefcSMark Murray 
1119b528cefcSMark Murray 	if (restart_point)
1120b528cefcSMark Murray 		mode = "r+";
1121b528cefcSMark Murray 	fout = fopen(name, mode);
1122b528cefcSMark Murray 	closefunc = fclose;
1123b528cefcSMark Murray 	if (fout == NULL) {
1124b528cefcSMark Murray 		perror_reply(553, name);
1125b528cefcSMark Murray 		LOGCMD(*mode == 'w' ? "put" : "append", name);
1126b528cefcSMark Murray 		return;
1127b528cefcSMark Murray 	}
1128b528cefcSMark Murray 	byte_count = -1;
1129b528cefcSMark Murray 	if (restart_point) {
1130b528cefcSMark Murray 		if (type == TYPE_A) {
1131b528cefcSMark Murray 			off_t i, n;
1132b528cefcSMark Murray 			int c;
1133b528cefcSMark Murray 
1134b528cefcSMark Murray 			n = restart_point;
1135b528cefcSMark Murray 			i = 0;
1136b528cefcSMark Murray 			while (i++ < n) {
1137b528cefcSMark Murray 				if ((c=getc(fout)) == EOF) {
1138b528cefcSMark Murray 					perror_reply(550, name);
1139b528cefcSMark Murray 					goto done;
1140b528cefcSMark Murray 				}
1141b528cefcSMark Murray 				if (c == '\n')
1142b528cefcSMark Murray 					i++;
1143b528cefcSMark Murray 			}
1144b528cefcSMark Murray 			/*
1145b528cefcSMark Murray 			 * We must do this seek to "current" position
1146b528cefcSMark Murray 			 * because we are changing from reading to
1147b528cefcSMark Murray 			 * writing.
1148b528cefcSMark Murray 			 */
1149b528cefcSMark Murray 			if (fseek(fout, 0L, SEEK_CUR) < 0) {
1150b528cefcSMark Murray 				perror_reply(550, name);
1151b528cefcSMark Murray 				goto done;
1152b528cefcSMark Murray 			}
1153b528cefcSMark Murray 		} else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1154b528cefcSMark Murray 			perror_reply(550, name);
1155b528cefcSMark Murray 			goto done;
1156b528cefcSMark Murray 		}
1157b528cefcSMark Murray 	}
1158b528cefcSMark Murray 	din = dataconn(name, (off_t)-1, "r");
1159b528cefcSMark Murray 	if (din == NULL)
1160b528cefcSMark Murray 		goto done;
1161b528cefcSMark Murray 	set_buffer_size(fileno(din), 1);
1162b528cefcSMark Murray 	if (receive_data(din, fout) == 0) {
1163adb0ddaeSAssar Westerlund 	    if((*closefunc)(fout) < 0)
1164adb0ddaeSAssar Westerlund 		perror_reply(552, name);
1165adb0ddaeSAssar Westerlund 	    else {
1166b528cefcSMark Murray 		if (unique)
1167b528cefcSMark Murray 			reply(226, "Transfer complete (unique file name:%s).",
1168b528cefcSMark Murray 			    name);
1169b528cefcSMark Murray 		else
1170b528cefcSMark Murray 			reply(226, "Transfer complete.");
1171b528cefcSMark Murray 	    }
1172adb0ddaeSAssar Westerlund 	} else
1173adb0ddaeSAssar Westerlund 	    (*closefunc)(fout);
1174b528cefcSMark Murray 	fclose(din);
1175b528cefcSMark Murray 	data = -1;
1176b528cefcSMark Murray 	pdata = -1;
1177b528cefcSMark Murray done:
1178b528cefcSMark Murray 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
1179b528cefcSMark Murray }
1180b528cefcSMark Murray 
1181b528cefcSMark Murray static FILE *
getdatasock(const char * mode,int domain)1182c19800e8SDoug Rabson getdatasock(const char *mode, int domain)
1183b528cefcSMark Murray {
1184b528cefcSMark Murray 	int s, t, tries;
1185b528cefcSMark Murray 
1186b528cefcSMark Murray 	if (data >= 0)
1187b528cefcSMark Murray 		return (fdopen(data, mode));
1188c19800e8SDoug Rabson 	if (seteuid(0) < 0)
1189c19800e8SDoug Rabson 		fatal("Failed to seteuid");
1190c19800e8SDoug Rabson 	s = socket(domain, SOCK_STREAM, 0);
1191b528cefcSMark Murray 	if (s < 0)
1192b528cefcSMark Murray 		goto bad;
1193b528cefcSMark Murray 	socket_set_reuseaddr (s, 1);
1194b528cefcSMark Murray 	/* anchor socket to avoid multi-homing problems */
1195b528cefcSMark Murray 	socket_set_address_and_port (data_source,
1196b528cefcSMark Murray 				     socket_get_address (ctrl_addr),
1197b528cefcSMark Murray 				     socket_get_port (data_source));
1198b528cefcSMark Murray 
1199b528cefcSMark Murray 	for (tries = 1; ; tries++) {
1200b528cefcSMark Murray 		if (bind(s, data_source,
1201b528cefcSMark Murray 			 socket_sockaddr_size (data_source)) >= 0)
1202b528cefcSMark Murray 			break;
1203b528cefcSMark Murray 		if (errno != EADDRINUSE || tries > 10)
1204b528cefcSMark Murray 			goto bad;
1205b528cefcSMark Murray 		sleep(tries);
1206b528cefcSMark Murray 	}
1207c19800e8SDoug Rabson 	if (seteuid(pw->pw_uid) < 0)
1208c19800e8SDoug Rabson 		fatal("Failed to seteuid");
1209b528cefcSMark Murray #ifdef IPTOS_THROUGHPUT
1210b528cefcSMark Murray 	socket_set_tos (s, IPTOS_THROUGHPUT);
1211b528cefcSMark Murray #endif
1212b528cefcSMark Murray 	return (fdopen(s, mode));
1213b528cefcSMark Murray bad:
1214b528cefcSMark Murray 	/* Return the real value of errno (close may change it) */
1215b528cefcSMark Murray 	t = errno;
1216c19800e8SDoug Rabson 	if (seteuid((uid_t)pw->pw_uid) < 0)
1217c19800e8SDoug Rabson 		fatal("Failed to seteuid");
1218b528cefcSMark Murray 	close(s);
1219b528cefcSMark Murray 	errno = t;
1220b528cefcSMark Murray 	return (NULL);
1221b528cefcSMark Murray }
1222b528cefcSMark Murray 
12234137ff4cSJacques Vidrine static int
accept_with_timeout(int socket,struct sockaddr * address,socklen_t * address_len,struct timeval * timeout)12244137ff4cSJacques Vidrine accept_with_timeout(int socket,
12254137ff4cSJacques Vidrine 		    struct sockaddr *address,
1226bbd80c28SJacques Vidrine 		    socklen_t *address_len,
12274137ff4cSJacques Vidrine 		    struct timeval *timeout)
12284137ff4cSJacques Vidrine {
12294137ff4cSJacques Vidrine     int ret;
12304137ff4cSJacques Vidrine     fd_set rfd;
12314137ff4cSJacques Vidrine     FD_ZERO(&rfd);
12324137ff4cSJacques Vidrine     FD_SET(socket, &rfd);
12334137ff4cSJacques Vidrine     ret = select(socket + 1, &rfd, NULL, NULL, timeout);
12344137ff4cSJacques Vidrine     if(ret < 0)
12354137ff4cSJacques Vidrine 	return ret;
12364137ff4cSJacques Vidrine     if(ret == 0) {
12374137ff4cSJacques Vidrine 	errno = ETIMEDOUT;
12384137ff4cSJacques Vidrine 	return -1;
12394137ff4cSJacques Vidrine     }
12404137ff4cSJacques Vidrine     return accept(socket, address, address_len);
12414137ff4cSJacques Vidrine }
12424137ff4cSJacques Vidrine 
1243b528cefcSMark Murray static FILE *
dataconn(const char * name,off_t size,const char * mode)1244b528cefcSMark Murray dataconn(const char *name, off_t size, const char *mode)
1245b528cefcSMark Murray {
1246b528cefcSMark Murray 	char sizebuf[32];
1247b528cefcSMark Murray 	FILE *file;
1248c19800e8SDoug Rabson 	int domain, retry = 0;
1249b528cefcSMark Murray 
1250b528cefcSMark Murray 	file_size = size;
1251b528cefcSMark Murray 	byte_count = 0;
1252b528cefcSMark Murray 	if (size >= 0)
1253b528cefcSMark Murray 	    snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", (long)size);
1254b528cefcSMark Murray 	else
1255b528cefcSMark Murray 	    *sizebuf = '\0';
1256b528cefcSMark Murray 	if (pdata >= 0) {
1257b528cefcSMark Murray 		struct sockaddr_storage from_ss;
1258b528cefcSMark Murray 		struct sockaddr *from = (struct sockaddr *)&from_ss;
12594137ff4cSJacques Vidrine 		struct timeval timeout;
1260b528cefcSMark Murray 		int s;
12615e9cd1aeSAssar Westerlund 		socklen_t fromlen = sizeof(from_ss);
1262b528cefcSMark Murray 
12634137ff4cSJacques Vidrine 		timeout.tv_sec = 15;
12644137ff4cSJacques Vidrine 		timeout.tv_usec = 0;
12654137ff4cSJacques Vidrine 		s = accept_with_timeout(pdata, from, &fromlen, &timeout);
1266b528cefcSMark Murray 		if (s < 0) {
1267b528cefcSMark Murray 			reply(425, "Can't open data connection.");
1268b528cefcSMark Murray 			close(pdata);
1269b528cefcSMark Murray 			pdata = -1;
1270b528cefcSMark Murray 			return (NULL);
1271b528cefcSMark Murray 		}
1272b528cefcSMark Murray 		close(pdata);
1273b528cefcSMark Murray 		pdata = s;
1274*ae771770SStanislav Sedov #if defined(IPTOS_THROUGHPUT)
1275*ae771770SStanislav Sedov 		if (from->sa_family == AF_INET)
1276*ae771770SStanislav Sedov 		    socket_set_tos(s, IPTOS_THROUGHPUT);
1277b528cefcSMark Murray #endif
1278b528cefcSMark Murray 		reply(150, "Opening %s mode data connection for '%s'%s.",
1279b528cefcSMark Murray 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1280b528cefcSMark Murray 		return (fdopen(pdata, mode));
1281b528cefcSMark Murray 	}
1282b528cefcSMark Murray 	if (data >= 0) {
1283b528cefcSMark Murray 		reply(125, "Using existing data connection for '%s'%s.",
1284b528cefcSMark Murray 		    name, sizebuf);
1285b528cefcSMark Murray 		usedefault = 1;
1286b528cefcSMark Murray 		return (fdopen(data, mode));
1287b528cefcSMark Murray 	}
1288b528cefcSMark Murray 	if (usedefault)
1289b528cefcSMark Murray 		data_dest = his_addr;
1290b528cefcSMark Murray 	usedefault = 1;
1291c19800e8SDoug Rabson 	/*
1292c19800e8SDoug Rabson 	 * Default to using the same socket type as the ctrl address,
1293c19800e8SDoug Rabson 	 * unless we know the type of the data address.
1294c19800e8SDoug Rabson 	 */
1295c19800e8SDoug Rabson 	domain = data_dest->sa_family;
1296c19800e8SDoug Rabson 	if (domain == PF_UNSPEC)
1297c19800e8SDoug Rabson 	    domain = ctrl_addr->sa_family;
1298c19800e8SDoug Rabson 
1299c19800e8SDoug Rabson 	file = getdatasock(mode, domain);
1300b528cefcSMark Murray 	if (file == NULL) {
1301b528cefcSMark Murray 		char data_addr[256];
1302b528cefcSMark Murray 
1303b528cefcSMark Murray 		if (inet_ntop (data_source->sa_family,
1304b528cefcSMark Murray 			       socket_get_address(data_source),
1305b528cefcSMark Murray 			       data_addr, sizeof(data_addr)) == NULL)
1306b528cefcSMark Murray 			strlcpy (data_addr, "unknown address",
1307b528cefcSMark Murray 					 sizeof(data_addr));
1308b528cefcSMark Murray 
1309b528cefcSMark Murray 		reply(425, "Can't create data socket (%s,%d): %s.",
1310b528cefcSMark Murray 		      data_addr,
1311b528cefcSMark Murray 		      socket_get_port (data_source),
1312b528cefcSMark Murray 		      strerror(errno));
1313b528cefcSMark Murray 		return (NULL);
1314b528cefcSMark Murray 	}
1315b528cefcSMark Murray 	data = fileno(file);
1316b528cefcSMark Murray 	while (connect(data, data_dest,
1317b528cefcSMark Murray 		       socket_sockaddr_size(data_dest)) < 0) {
1318b528cefcSMark Murray 		if (errno == EADDRINUSE && retry < swaitmax) {
1319b528cefcSMark Murray 			sleep(swaitint);
1320b528cefcSMark Murray 			retry += swaitint;
1321b528cefcSMark Murray 			continue;
1322b528cefcSMark Murray 		}
1323b528cefcSMark Murray 		perror_reply(425, "Can't build data connection");
1324b528cefcSMark Murray 		fclose(file);
1325b528cefcSMark Murray 		data = -1;
1326b528cefcSMark Murray 		return (NULL);
1327b528cefcSMark Murray 	}
1328b528cefcSMark Murray 	reply(150, "Opening %s mode data connection for '%s'%s.",
1329b528cefcSMark Murray 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1330b528cefcSMark Murray 	return (file);
1331b528cefcSMark Murray }
1332b528cefcSMark Murray 
1333b528cefcSMark Murray /*
1334b528cefcSMark Murray  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1335b528cefcSMark Murray  * encapsulation of the data subject * to Mode, Structure, and Type.
1336b528cefcSMark Murray  *
1337b528cefcSMark Murray  * NB: Form isn't handled.
1338b528cefcSMark Murray  */
1339b528cefcSMark Murray static void
send_data(FILE * instr,FILE * outstr)1340b528cefcSMark Murray send_data(FILE *instr, FILE *outstr)
1341b528cefcSMark Murray {
1342b528cefcSMark Murray 	int c, cnt, filefd, netfd;
1343b528cefcSMark Murray 	static char *buf;
1344b528cefcSMark Murray 	static size_t bufsize;
1345b528cefcSMark Murray 
13468d4ba808SJacques Vidrine 	transflag = 1;
1347b528cefcSMark Murray 	switch (type) {
1348b528cefcSMark Murray 
1349b528cefcSMark Murray 	case TYPE_A:
1350b528cefcSMark Murray 	    while ((c = getc(instr)) != EOF) {
13518d4ba808SJacques Vidrine 		if (urgflag && handleoobcmd())
13528d4ba808SJacques Vidrine 		    return;
1353b528cefcSMark Murray 		byte_count++;
1354b528cefcSMark Murray 		if(c == '\n')
1355b528cefcSMark Murray 		    sec_putc('\r', outstr);
1356b528cefcSMark Murray 		sec_putc(c, outstr);
1357b528cefcSMark Murray 	    }
1358b528cefcSMark Murray 	    sec_fflush(outstr);
1359b528cefcSMark Murray 	    transflag = 0;
13608d4ba808SJacques Vidrine 	    urgflag = 0;
1361b528cefcSMark Murray 	    if (ferror(instr))
1362b528cefcSMark Murray 		goto file_err;
1363b528cefcSMark Murray 	    if (ferror(outstr))
1364b528cefcSMark Murray 		goto data_err;
1365b528cefcSMark Murray 	    reply(226, "Transfer complete.");
1366b528cefcSMark Murray 	    return;
1367b528cefcSMark Murray 
1368b528cefcSMark Murray 	case TYPE_I:
1369b528cefcSMark Murray 	case TYPE_L:
13708d4ba808SJacques Vidrine #if 0 /* XXX handle urg flag */
1371b528cefcSMark Murray #if defined(HAVE_MMAP) && !defined(NO_MMAP)
1372b528cefcSMark Murray #ifndef MAP_FAILED
1373b528cefcSMark Murray #define MAP_FAILED (-1)
1374b528cefcSMark Murray #endif
1375b528cefcSMark Murray 	    {
1376b528cefcSMark Murray 		struct stat st;
1377b528cefcSMark Murray 		char *chunk;
1378b528cefcSMark Murray 		int in = fileno(instr);
1379b528cefcSMark Murray 		if(fstat(in, &st) == 0 && S_ISREG(st.st_mode)
1380b528cefcSMark Murray 		   && st.st_size > 0) {
1381b528cefcSMark Murray 		    /*
1382b528cefcSMark Murray 		     * mmap zero bytes has potential of loosing, don't do it.
1383b528cefcSMark Murray 		     */
1384b528cefcSMark Murray 		    chunk = mmap(0, st.st_size, PROT_READ,
1385b528cefcSMark Murray 				 MAP_SHARED, in, 0);
1386b528cefcSMark Murray 		    if((void *)chunk != (void *)MAP_FAILED) {
1387b528cefcSMark Murray 			cnt = st.st_size - restart_point;
1388b528cefcSMark Murray 			sec_write(fileno(outstr), chunk + restart_point, cnt);
1389b528cefcSMark Murray 			if (munmap(chunk, st.st_size) < 0)
1390b528cefcSMark Murray 			    warn ("munmap");
1391b528cefcSMark Murray 			sec_fflush(outstr);
1392b528cefcSMark Murray 			byte_count = cnt;
1393b528cefcSMark Murray 			transflag = 0;
13948d4ba808SJacques Vidrine 			urgflag = 0;
1395b528cefcSMark Murray 		    }
1396b528cefcSMark Murray 		}
1397b528cefcSMark Murray 	    }
1398b528cefcSMark Murray #endif
13998d4ba808SJacques Vidrine #endif
1400b528cefcSMark Murray 	if(transflag) {
1401b528cefcSMark Murray 	    struct stat st;
1402b528cefcSMark Murray 
1403b528cefcSMark Murray 	    netfd = fileno(outstr);
1404b528cefcSMark Murray 	    filefd = fileno(instr);
1405b528cefcSMark Murray 	    buf = alloc_buffer (buf, &bufsize,
1406b528cefcSMark Murray 				fstat(filefd, &st) >= 0 ? &st : NULL);
1407b528cefcSMark Murray 	    if (buf == NULL) {
1408b528cefcSMark Murray 		transflag = 0;
14098d4ba808SJacques Vidrine 		urgflag = 0;
1410b528cefcSMark Murray 		perror_reply(451, "Local resource failure: malloc");
1411b528cefcSMark Murray 		return;
1412b528cefcSMark Murray 	    }
1413b528cefcSMark Murray 	    while ((cnt = read(filefd, buf, bufsize)) > 0 &&
14148d4ba808SJacques Vidrine 		   sec_write(netfd, buf, cnt) == cnt) {
1415b528cefcSMark Murray 		byte_count += cnt;
14168d4ba808SJacques Vidrine 		if (urgflag && handleoobcmd())
14178d4ba808SJacques Vidrine 		    return;
14188d4ba808SJacques Vidrine 	    }
1419b528cefcSMark Murray 	    sec_fflush(outstr); /* to end an encrypted stream */
1420b528cefcSMark Murray 	    transflag = 0;
14218d4ba808SJacques Vidrine 	    urgflag = 0;
1422b528cefcSMark Murray 	    if (cnt != 0) {
1423b528cefcSMark Murray 		if (cnt < 0)
1424b528cefcSMark Murray 		    goto file_err;
1425b528cefcSMark Murray 		goto data_err;
1426b528cefcSMark Murray 	    }
1427b528cefcSMark Murray 	}
1428b528cefcSMark Murray 	reply(226, "Transfer complete.");
1429b528cefcSMark Murray 	return;
1430b528cefcSMark Murray 	default:
1431b528cefcSMark Murray 	    transflag = 0;
14328d4ba808SJacques Vidrine 	    urgflag = 0;
1433b528cefcSMark Murray 	    reply(550, "Unimplemented TYPE %d in send_data", type);
1434b528cefcSMark Murray 	    return;
1435b528cefcSMark Murray 	}
1436b528cefcSMark Murray 
1437b528cefcSMark Murray data_err:
1438b528cefcSMark Murray 	transflag = 0;
14398d4ba808SJacques Vidrine 	urgflag = 0;
1440b528cefcSMark Murray 	perror_reply(426, "Data connection");
1441b528cefcSMark Murray 	return;
1442b528cefcSMark Murray 
1443b528cefcSMark Murray file_err:
1444b528cefcSMark Murray 	transflag = 0;
14458d4ba808SJacques Vidrine 	urgflag = 0;
1446b528cefcSMark Murray 	perror_reply(551, "Error on input file");
1447b528cefcSMark Murray }
1448b528cefcSMark Murray 
1449b528cefcSMark Murray /*
1450b528cefcSMark Murray  * Transfer data from peer to "outstr" using the appropriate encapulation of
1451b528cefcSMark Murray  * the data subject to Mode, Structure, and Type.
1452b528cefcSMark Murray  *
1453b528cefcSMark Murray  * N.B.: Form isn't handled.
1454b528cefcSMark Murray  */
1455b528cefcSMark Murray static int
receive_data(FILE * instr,FILE * outstr)1456b528cefcSMark Murray receive_data(FILE *instr, FILE *outstr)
1457b528cefcSMark Murray {
1458b528cefcSMark Murray     int cnt, bare_lfs = 0;
1459b528cefcSMark Murray     static char *buf;
1460b528cefcSMark Murray     static size_t bufsize;
1461b528cefcSMark Murray     struct stat st;
1462b528cefcSMark Murray 
14638d4ba808SJacques Vidrine     transflag = 1;
1464b528cefcSMark Murray 
1465b528cefcSMark Murray     buf = alloc_buffer (buf, &bufsize,
1466b528cefcSMark Murray 			fstat(fileno(outstr), &st) >= 0 ? &st : NULL);
1467b528cefcSMark Murray     if (buf == NULL) {
1468b528cefcSMark Murray 	transflag = 0;
14698d4ba808SJacques Vidrine 	urgflag = 0;
1470b528cefcSMark Murray 	perror_reply(451, "Local resource failure: malloc");
1471b528cefcSMark Murray 	return -1;
1472b528cefcSMark Murray     }
1473b528cefcSMark Murray 
1474b528cefcSMark Murray     switch (type) {
1475b528cefcSMark Murray 
1476b528cefcSMark Murray     case TYPE_I:
1477b528cefcSMark Murray     case TYPE_L:
1478b528cefcSMark Murray 	while ((cnt = sec_read(fileno(instr), buf, bufsize)) > 0) {
1479b528cefcSMark Murray 	    if (write(fileno(outstr), buf, cnt) != cnt)
1480b528cefcSMark Murray 		goto file_err;
1481b528cefcSMark Murray 	    byte_count += cnt;
14828d4ba808SJacques Vidrine 	    if (urgflag && handleoobcmd())
14838d4ba808SJacques Vidrine 		return (-1);
1484b528cefcSMark Murray 	}
1485b528cefcSMark Murray 	if (cnt < 0)
1486b528cefcSMark Murray 	    goto data_err;
1487b528cefcSMark Murray 	transflag = 0;
14888d4ba808SJacques Vidrine 	urgflag = 0;
1489b528cefcSMark Murray 	return (0);
1490b528cefcSMark Murray 
1491b528cefcSMark Murray     case TYPE_E:
1492b528cefcSMark Murray 	reply(553, "TYPE E not implemented.");
1493b528cefcSMark Murray 	transflag = 0;
14948d4ba808SJacques Vidrine 	urgflag = 0;
1495b528cefcSMark Murray 	return (-1);
1496b528cefcSMark Murray 
1497b528cefcSMark Murray     case TYPE_A:
1498b528cefcSMark Murray     {
1499b528cefcSMark Murray 	char *p, *q;
1500b528cefcSMark Murray 	int cr_flag = 0;
1501b528cefcSMark Murray 	while ((cnt = sec_read(fileno(instr),
1502b528cefcSMark Murray 				buf + cr_flag,
1503b528cefcSMark Murray 				bufsize - cr_flag)) > 0){
15048d4ba808SJacques Vidrine 	    if (urgflag && handleoobcmd())
15058d4ba808SJacques Vidrine 		return (-1);
1506b528cefcSMark Murray 	    byte_count += cnt;
1507b528cefcSMark Murray 	    cnt += cr_flag;
1508b528cefcSMark Murray 	    cr_flag = 0;
1509b528cefcSMark Murray 	    for(p = buf, q = buf; p < buf + cnt;) {
1510b528cefcSMark Murray 		if(*p == '\n')
1511b528cefcSMark Murray 		    bare_lfs++;
1512b528cefcSMark Murray 		if(*p == '\r') {
1513b528cefcSMark Murray 		    if(p == buf + cnt - 1){
1514b528cefcSMark Murray 			cr_flag = 1;
1515b528cefcSMark Murray 			p++;
1516b528cefcSMark Murray 			continue;
1517b528cefcSMark Murray 		    }else if(p[1] == '\n'){
1518b528cefcSMark Murray 			*q++ = '\n';
1519b528cefcSMark Murray 			p += 2;
1520b528cefcSMark Murray 			continue;
1521b528cefcSMark Murray 		    }
1522b528cefcSMark Murray 		}
1523b528cefcSMark Murray 		*q++ = *p++;
1524b528cefcSMark Murray 	    }
1525b528cefcSMark Murray 	    fwrite(buf, q - buf, 1, outstr);
1526b528cefcSMark Murray 	    if(cr_flag)
1527b528cefcSMark Murray 		buf[0] = '\r';
1528b528cefcSMark Murray 	}
1529b528cefcSMark Murray 	if(cr_flag)
1530b528cefcSMark Murray 	    putc('\r', outstr);
1531b528cefcSMark Murray 	fflush(outstr);
1532b528cefcSMark Murray 	if (ferror(instr))
1533b528cefcSMark Murray 	    goto data_err;
1534b528cefcSMark Murray 	if (ferror(outstr))
1535b528cefcSMark Murray 	    goto file_err;
1536b528cefcSMark Murray 	transflag = 0;
15378d4ba808SJacques Vidrine 	urgflag = 0;
1538b528cefcSMark Murray 	if (bare_lfs) {
1539b528cefcSMark Murray 	    lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n"
1540b528cefcSMark Murray 		   "    File may not have transferred correctly.\r\n",
1541b528cefcSMark Murray 		   bare_lfs);
1542b528cefcSMark Murray 	}
1543b528cefcSMark Murray 	return (0);
1544b528cefcSMark Murray     }
1545b528cefcSMark Murray     default:
1546b528cefcSMark Murray 	reply(550, "Unimplemented TYPE %d in receive_data", type);
1547b528cefcSMark Murray 	transflag = 0;
15488d4ba808SJacques Vidrine 	urgflag = 0;
1549b528cefcSMark Murray 	return (-1);
1550b528cefcSMark Murray     }
1551b528cefcSMark Murray 
1552b528cefcSMark Murray data_err:
1553b528cefcSMark Murray     transflag = 0;
15548d4ba808SJacques Vidrine     urgflag = 0;
1555b528cefcSMark Murray     perror_reply(426, "Data Connection");
1556b528cefcSMark Murray     return (-1);
1557b528cefcSMark Murray 
1558b528cefcSMark Murray file_err:
1559b528cefcSMark Murray     transflag = 0;
15608d4ba808SJacques Vidrine     urgflag = 0;
1561b528cefcSMark Murray     perror_reply(452, "Error writing file");
1562b528cefcSMark Murray     return (-1);
1563b528cefcSMark Murray }
1564b528cefcSMark Murray 
1565b528cefcSMark Murray void
statfilecmd(char * filename)1566b528cefcSMark Murray statfilecmd(char *filename)
1567b528cefcSMark Murray {
1568b528cefcSMark Murray 	FILE *fin;
1569b528cefcSMark Murray 	int c;
1570b528cefcSMark Murray 	char line[LINE_MAX];
1571b528cefcSMark Murray 
1572b528cefcSMark Murray 	snprintf(line, sizeof(line), "/bin/ls -la -- %s", filename);
1573b528cefcSMark Murray 	fin = ftpd_popen(line, "r", 1, 0);
1574b528cefcSMark Murray 	lreply(211, "status of %s:", filename);
1575b528cefcSMark Murray 	while ((c = getc(fin)) != EOF) {
1576b528cefcSMark Murray 		if (c == '\n') {
1577b528cefcSMark Murray 			if (ferror(stdout)){
1578b528cefcSMark Murray 				perror_reply(421, "control connection");
1579b528cefcSMark Murray 				ftpd_pclose(fin);
1580b528cefcSMark Murray 				dologout(1);
1581b528cefcSMark Murray 				/* NOTREACHED */
1582b528cefcSMark Murray 			}
1583b528cefcSMark Murray 			if (ferror(fin)) {
1584b528cefcSMark Murray 				perror_reply(551, filename);
1585b528cefcSMark Murray 				ftpd_pclose(fin);
1586b528cefcSMark Murray 				return;
1587b528cefcSMark Murray 			}
1588b528cefcSMark Murray 			putc('\r', stdout);
1589b528cefcSMark Murray 		}
1590b528cefcSMark Murray 		putc(c, stdout);
1591b528cefcSMark Murray 	}
1592b528cefcSMark Murray 	ftpd_pclose(fin);
1593b528cefcSMark Murray 	reply(211, "End of Status");
1594b528cefcSMark Murray }
1595b528cefcSMark Murray 
1596b528cefcSMark Murray void
statcmd(void)1597b528cefcSMark Murray statcmd(void)
1598b528cefcSMark Murray {
1599b528cefcSMark Murray #if 0
1600b528cefcSMark Murray 	struct sockaddr_in *sin;
1601b528cefcSMark Murray 	u_char *a, *p;
1602b528cefcSMark Murray 
1603b528cefcSMark Murray 	lreply(211, "%s FTP server (%s) status:", hostname, version);
1604b528cefcSMark Murray 	printf("     %s\r\n", version);
1605b528cefcSMark Murray 	printf("     Connected to %s", remotehost);
1606c19800e8SDoug Rabson 	if (!isdigit((unsigned char)remotehost[0]))
1607b528cefcSMark Murray 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1608b528cefcSMark Murray 	printf("\r\n");
1609b528cefcSMark Murray 	if (logged_in) {
1610b528cefcSMark Murray 		if (guest)
1611b528cefcSMark Murray 			printf("     Logged in anonymously\r\n");
1612b528cefcSMark Murray 		else
1613b528cefcSMark Murray 			printf("     Logged in as %s\r\n", pw->pw_name);
1614b528cefcSMark Murray 	} else if (askpasswd)
1615b528cefcSMark Murray 		printf("     Waiting for password\r\n");
1616b528cefcSMark Murray 	else
1617b528cefcSMark Murray 		printf("     Waiting for user name\r\n");
1618b528cefcSMark Murray 	printf("     TYPE: %s", typenames[type]);
1619b528cefcSMark Murray 	if (type == TYPE_A || type == TYPE_E)
1620b528cefcSMark Murray 		printf(", FORM: %s", formnames[form]);
1621b528cefcSMark Murray 	if (type == TYPE_L)
1622b528cefcSMark Murray #if NBBY == 8
1623b528cefcSMark Murray 		printf(" %d", NBBY);
1624b528cefcSMark Murray #else
1625b528cefcSMark Murray 		printf(" %d", bytesize);	/* need definition! */
1626b528cefcSMark Murray #endif
1627b528cefcSMark Murray 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1628b528cefcSMark Murray 	    strunames[stru], modenames[mode]);
1629b528cefcSMark Murray 	if (data != -1)
1630b528cefcSMark Murray 		printf("     Data connection open\r\n");
1631b528cefcSMark Murray 	else if (pdata != -1) {
1632b528cefcSMark Murray 		printf("     in Passive mode");
1633b528cefcSMark Murray 		sin = &pasv_addr;
1634b528cefcSMark Murray 		goto printaddr;
1635b528cefcSMark Murray 	} else if (usedefault == 0) {
1636b528cefcSMark Murray 		printf("     PORT");
1637b528cefcSMark Murray 		sin = &data_dest;
1638b528cefcSMark Murray printaddr:
1639b528cefcSMark Murray 		a = (u_char *) &sin->sin_addr;
1640b528cefcSMark Murray 		p = (u_char *) &sin->sin_port;
1641b528cefcSMark Murray #define UC(b) (((int) b) & 0xff)
1642b528cefcSMark Murray 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1643b528cefcSMark Murray 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1644b528cefcSMark Murray #undef UC
1645b528cefcSMark Murray 	} else
1646b528cefcSMark Murray 		printf("     No data connection\r\n");
1647b528cefcSMark Murray #endif
1648b528cefcSMark Murray 	reply(211, "End of status");
1649b528cefcSMark Murray }
1650b528cefcSMark Murray 
1651b528cefcSMark Murray void
fatal(char * s)1652b528cefcSMark Murray fatal(char *s)
1653b528cefcSMark Murray {
1654b528cefcSMark Murray 
1655b528cefcSMark Murray 	reply(451, "Error in server: %s\n", s);
1656b528cefcSMark Murray 	reply(221, "Closing connection due to server error.");
1657b528cefcSMark Murray 	dologout(0);
1658b528cefcSMark Murray 	/* NOTREACHED */
1659b528cefcSMark Murray }
1660b528cefcSMark Murray 
1661b528cefcSMark Murray static void
1662b528cefcSMark Murray int_reply(int, char *, const char *, va_list)
1663b528cefcSMark Murray #ifdef __GNUC__
1664b528cefcSMark Murray __attribute__ ((format (printf, 3, 0)))
1665b528cefcSMark Murray #endif
1666b528cefcSMark Murray ;
1667b528cefcSMark Murray 
1668b528cefcSMark Murray static void
int_reply(int n,char * c,const char * fmt,va_list ap)1669b528cefcSMark Murray int_reply(int n, char *c, const char *fmt, va_list ap)
1670b528cefcSMark Murray {
1671b528cefcSMark Murray     char buf[10240];
1672b528cefcSMark Murray     char *p;
1673b528cefcSMark Murray     p=buf;
1674b528cefcSMark Murray     if(n){
1675b528cefcSMark Murray 	snprintf(p, sizeof(buf), "%d%s", n, c);
1676b528cefcSMark Murray 	p+=strlen(p);
1677b528cefcSMark Murray     }
1678b528cefcSMark Murray     vsnprintf(p, sizeof(buf) - strlen(p), fmt, ap);
1679b528cefcSMark Murray     p+=strlen(p);
1680b528cefcSMark Murray     snprintf(p, sizeof(buf) - strlen(p), "\r\n");
1681b528cefcSMark Murray     p+=strlen(p);
1682b528cefcSMark Murray     sec_fprintf(stdout, "%s", buf);
1683b528cefcSMark Murray     fflush(stdout);
1684b528cefcSMark Murray     if (debug)
1685b528cefcSMark Murray 	syslog(LOG_DEBUG, "<--- %s- ", buf);
1686b528cefcSMark Murray }
1687b528cefcSMark Murray 
1688b528cefcSMark Murray void
reply(int n,const char * fmt,...)1689b528cefcSMark Murray reply(int n, const char *fmt, ...)
1690b528cefcSMark Murray {
1691b528cefcSMark Murray   va_list ap;
1692b528cefcSMark Murray   va_start(ap, fmt);
1693b528cefcSMark Murray   int_reply(n, " ", fmt, ap);
1694b528cefcSMark Murray   delete_ftp_command();
1695b528cefcSMark Murray   va_end(ap);
1696b528cefcSMark Murray }
1697b528cefcSMark Murray 
1698b528cefcSMark Murray void
lreply(int n,const char * fmt,...)1699b528cefcSMark Murray lreply(int n, const char *fmt, ...)
1700b528cefcSMark Murray {
1701b528cefcSMark Murray   va_list ap;
1702b528cefcSMark Murray   va_start(ap, fmt);
1703b528cefcSMark Murray   int_reply(n, "-", fmt, ap);
1704b528cefcSMark Murray   va_end(ap);
1705b528cefcSMark Murray }
1706b528cefcSMark Murray 
1707b528cefcSMark Murray void
nreply(const char * fmt,...)1708b528cefcSMark Murray nreply(const char *fmt, ...)
1709b528cefcSMark Murray {
1710b528cefcSMark Murray   va_list ap;
1711b528cefcSMark Murray   va_start(ap, fmt);
1712b528cefcSMark Murray   int_reply(0, NULL, fmt, ap);
1713b528cefcSMark Murray   va_end(ap);
1714b528cefcSMark Murray }
1715b528cefcSMark Murray 
1716b528cefcSMark Murray static void
ack(char * s)1717b528cefcSMark Murray ack(char *s)
1718b528cefcSMark Murray {
1719b528cefcSMark Murray 
1720b528cefcSMark Murray 	reply(250, "%s command successful.", s);
1721b528cefcSMark Murray }
1722b528cefcSMark Murray 
1723b528cefcSMark Murray void
nack(char * s)1724b528cefcSMark Murray nack(char *s)
1725b528cefcSMark Murray {
1726b528cefcSMark Murray 
1727b528cefcSMark Murray 	reply(502, "%s command not implemented.", s);
1728b528cefcSMark Murray }
1729b528cefcSMark Murray 
1730b528cefcSMark Murray void
do_delete(char * name)1731b528cefcSMark Murray do_delete(char *name)
1732b528cefcSMark Murray {
1733b528cefcSMark Murray 	struct stat st;
1734b528cefcSMark Murray 
1735b528cefcSMark Murray 	LOGCMD("delete", name);
1736b528cefcSMark Murray 	if (stat(name, &st) < 0) {
1737b528cefcSMark Murray 		perror_reply(550, name);
1738b528cefcSMark Murray 		return;
1739b528cefcSMark Murray 	}
1740*ae771770SStanislav Sedov 	if (S_ISDIR(st.st_mode)) {
1741b528cefcSMark Murray 		if (rmdir(name) < 0) {
1742b528cefcSMark Murray 			perror_reply(550, name);
1743b528cefcSMark Murray 			return;
1744b528cefcSMark Murray 		}
1745b528cefcSMark Murray 		goto done;
1746b528cefcSMark Murray 	}
1747b528cefcSMark Murray 	if (unlink(name) < 0) {
1748b528cefcSMark Murray 		perror_reply(550, name);
1749b528cefcSMark Murray 		return;
1750b528cefcSMark Murray 	}
1751b528cefcSMark Murray done:
1752b528cefcSMark Murray 	ack("DELE");
1753b528cefcSMark Murray }
1754b528cefcSMark Murray 
1755b528cefcSMark Murray void
cwd(const char * path)1756*ae771770SStanislav Sedov cwd(const char *path)
1757b528cefcSMark Murray {
1758b528cefcSMark Murray 
1759b528cefcSMark Murray 	if (chdir(path) < 0)
1760b528cefcSMark Murray 		perror_reply(550, path);
1761b528cefcSMark Murray 	else
1762b528cefcSMark Murray 		ack("CWD");
1763b528cefcSMark Murray }
1764b528cefcSMark Murray 
1765b528cefcSMark Murray void
makedir(char * name)1766b528cefcSMark Murray makedir(char *name)
1767b528cefcSMark Murray {
1768b528cefcSMark Murray 
1769b528cefcSMark Murray 	LOGCMD("mkdir", name);
1770b528cefcSMark Murray 	if(guest && filename_check(name))
1771b528cefcSMark Murray 	    return;
1772b528cefcSMark Murray 	if (mkdir(name, 0777) < 0)
1773b528cefcSMark Murray 		perror_reply(550, name);
1774b528cefcSMark Murray 	else{
1775b528cefcSMark Murray 	    if(guest)
1776b528cefcSMark Murray 		chmod(name, 0700); /* guest has umask 777 */
1777b528cefcSMark Murray 	    reply(257, "MKD command successful.");
1778b528cefcSMark Murray 	}
1779b528cefcSMark Murray }
1780b528cefcSMark Murray 
1781b528cefcSMark Murray void
removedir(char * name)1782b528cefcSMark Murray removedir(char *name)
1783b528cefcSMark Murray {
1784b528cefcSMark Murray 
1785b528cefcSMark Murray 	LOGCMD("rmdir", name);
1786b528cefcSMark Murray 	if (rmdir(name) < 0)
1787b528cefcSMark Murray 		perror_reply(550, name);
1788b528cefcSMark Murray 	else
1789b528cefcSMark Murray 		ack("RMD");
1790b528cefcSMark Murray }
1791b528cefcSMark Murray 
1792b528cefcSMark Murray void
pwd(void)1793b528cefcSMark Murray pwd(void)
1794b528cefcSMark Murray {
1795b528cefcSMark Murray     char path[MaxPathLen];
1796b528cefcSMark Murray     char *ret;
1797b528cefcSMark Murray 
1798b528cefcSMark Murray     /* SunOS has a broken getcwd that does popen(pwd) (!!!), this
1799b528cefcSMark Murray      * failes miserably when running chroot
1800b528cefcSMark Murray      */
1801b528cefcSMark Murray     ret = getcwd(path, sizeof(path));
1802b528cefcSMark Murray     if (ret == NULL)
1803b528cefcSMark Murray 	reply(550, "%s.", strerror(errno));
1804b528cefcSMark Murray     else
1805b528cefcSMark Murray 	reply(257, "\"%s\" is current directory.", path);
1806b528cefcSMark Murray }
1807b528cefcSMark Murray 
1808b528cefcSMark Murray char *
renamefrom(char * name)1809b528cefcSMark Murray renamefrom(char *name)
1810b528cefcSMark Murray {
1811b528cefcSMark Murray 	struct stat st;
1812b528cefcSMark Murray 
1813b528cefcSMark Murray 	if (stat(name, &st) < 0) {
1814b528cefcSMark Murray 		perror_reply(550, name);
1815b528cefcSMark Murray 		return NULL;
1816b528cefcSMark Murray 	}
1817b528cefcSMark Murray 	reply(350, "File exists, ready for destination name");
1818b528cefcSMark Murray 	return (name);
1819b528cefcSMark Murray }
1820b528cefcSMark Murray 
1821b528cefcSMark Murray void
renamecmd(char * from,char * to)1822b528cefcSMark Murray renamecmd(char *from, char *to)
1823b528cefcSMark Murray {
1824b528cefcSMark Murray 
1825b528cefcSMark Murray 	LOGCMD2("rename", from, to);
1826b528cefcSMark Murray 	if(guest && filename_check(to))
1827b528cefcSMark Murray 	    return;
1828b528cefcSMark Murray 	if (rename(from, to) < 0)
1829b528cefcSMark Murray 		perror_reply(550, "rename");
1830b528cefcSMark Murray 	else
1831b528cefcSMark Murray 		ack("RNTO");
1832b528cefcSMark Murray }
1833b528cefcSMark Murray 
1834b528cefcSMark Murray static void
dolog(struct sockaddr * sa,int len)1835b528cefcSMark Murray dolog(struct sockaddr *sa, int len)
1836b528cefcSMark Murray {
1837b528cefcSMark Murray 	getnameinfo_verified (sa, len, remotehost, sizeof(remotehost),
1838b528cefcSMark Murray 			      NULL, 0, 0);
1839b528cefcSMark Murray #ifdef HAVE_SETPROCTITLE
1840b528cefcSMark Murray 	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1841b904de74SKris Kennaway 	setproctitle("%s", proctitle);
1842b528cefcSMark Murray #endif /* HAVE_SETPROCTITLE */
1843b528cefcSMark Murray 
1844b528cefcSMark Murray 	if (logging) {
1845b528cefcSMark Murray 		char data_addr[256];
1846b528cefcSMark Murray 
1847b528cefcSMark Murray 		if (inet_ntop (his_addr->sa_family,
1848b528cefcSMark Murray 			       socket_get_address(his_addr),
1849b528cefcSMark Murray 			       data_addr, sizeof(data_addr)) == NULL)
1850b528cefcSMark Murray 			strlcpy (data_addr, "unknown address",
1851b528cefcSMark Murray 					 sizeof(data_addr));
1852b528cefcSMark Murray 
1853b528cefcSMark Murray 
1854b528cefcSMark Murray 		syslog(LOG_INFO, "connection from %s(%s)",
1855b528cefcSMark Murray 		       remotehost,
1856b528cefcSMark Murray 		       data_addr);
1857b528cefcSMark Murray 	}
1858b528cefcSMark Murray }
1859b528cefcSMark Murray 
1860b528cefcSMark Murray /*
1861b528cefcSMark Murray  * Record logout in wtmp file
1862b528cefcSMark Murray  * and exit with supplied status.
1863b528cefcSMark Murray  */
1864b528cefcSMark Murray void
dologout(int status)1865b528cefcSMark Murray dologout(int status)
1866b528cefcSMark Murray {
1867b528cefcSMark Murray     transflag = 0;
18688d4ba808SJacques Vidrine     urgflag = 0;
1869b528cefcSMark Murray     if (logged_in) {
1870*ae771770SStanislav Sedov #if KRB5
1871b528cefcSMark Murray 	cond_kdestroy();
1872b528cefcSMark Murray #endif
1873c19800e8SDoug Rabson 	seteuid((uid_t)0); /* No need to check, we call exit() below */
1874c19800e8SDoug Rabson 	ftpd_logwtmp(ttyline, "", "");
1875b528cefcSMark Murray     }
1876b528cefcSMark Murray     /* beware of flushing buffers after a SIGPIPE */
1877b528cefcSMark Murray #ifdef XXX
1878b528cefcSMark Murray     exit(status);
1879b528cefcSMark Murray #else
1880b528cefcSMark Murray     _exit(status);
1881b528cefcSMark Murray #endif
1882b528cefcSMark Murray }
1883b528cefcSMark Murray 
abor(void)1884b528cefcSMark Murray void abor(void)
1885b528cefcSMark Murray {
18868d4ba808SJacques Vidrine     if (!transflag)
18878d4ba808SJacques Vidrine 	return;
18888d4ba808SJacques Vidrine     reply(426, "Transfer aborted. Data connection closed.");
18898d4ba808SJacques Vidrine     reply(226, "Abort successful");
18908d4ba808SJacques Vidrine     transflag = 0;
1891b528cefcSMark Murray }
1892b528cefcSMark Murray 
1893b528cefcSMark Murray static void
myoob(int signo)1894b528cefcSMark Murray myoob(int signo)
1895b528cefcSMark Murray {
18968d4ba808SJacques Vidrine     urgflag = 1;
18978d4ba808SJacques Vidrine }
18988d4ba808SJacques Vidrine 
18998d4ba808SJacques Vidrine static char *
mec_space(char * p)19008d4ba808SJacques Vidrine mec_space(char *p)
19018d4ba808SJacques Vidrine {
19028d4ba808SJacques Vidrine     while(isspace(*(unsigned char *)p))
19038d4ba808SJacques Vidrine 	  p++;
19048d4ba808SJacques Vidrine     return p;
19058d4ba808SJacques Vidrine }
19068d4ba808SJacques Vidrine 
19078d4ba808SJacques Vidrine static int
handleoobcmd(void)19088d4ba808SJacques Vidrine handleoobcmd(void)
19098d4ba808SJacques Vidrine {
1910b528cefcSMark Murray 	char *cp;
1911b528cefcSMark Murray 
1912b528cefcSMark Murray 	/* only process if transfer occurring */
1913b528cefcSMark Murray 	if (!transflag)
19148d4ba808SJacques Vidrine 		return 0;
1915b528cefcSMark Murray 
19168d4ba808SJacques Vidrine 	urgflag = 0;
1917b528cefcSMark Murray 
1918b528cefcSMark Murray 	cp = tmpline;
19198d4ba808SJacques Vidrine 	if (ftpd_getline(cp, sizeof(tmpline)) == NULL) {
1920b528cefcSMark Murray 		reply(221, "You could at least say goodbye.");
1921b528cefcSMark Murray 		dologout(0);
1922b528cefcSMark Murray 	}
19238d4ba808SJacques Vidrine 
19248d4ba808SJacques Vidrine 	if (strncasecmp("MIC", cp, 3) == 0) {
19258d4ba808SJacques Vidrine 	    mec(mec_space(cp + 3), prot_safe);
19268d4ba808SJacques Vidrine 	} else if (strncasecmp("CONF", cp, 4) == 0) {
19278d4ba808SJacques Vidrine 	    mec(mec_space(cp + 4), prot_confidential);
19288d4ba808SJacques Vidrine 	} else if (strncasecmp("ENC", cp, 3) == 0) {
19298d4ba808SJacques Vidrine 	    mec(mec_space(cp + 3), prot_private);
19308d4ba808SJacques Vidrine 	} else if (!allow_insecure_oob) {
19318d4ba808SJacques Vidrine 	    reply(533, "Command protection level denied "
19328d4ba808SJacques Vidrine 		  "for paranoid reasons.");
19338d4ba808SJacques Vidrine 	    goto out;
1934b528cefcSMark Murray 	}
19358d4ba808SJacques Vidrine 
19368d4ba808SJacques Vidrine 	if (secure_command())
19378d4ba808SJacques Vidrine 	    cp = ftp_command;
19388d4ba808SJacques Vidrine 
19398d4ba808SJacques Vidrine 	if (strcasecmp(cp, "ABOR\r\n") == 0) {
19408d4ba808SJacques Vidrine 		abor();
19418d4ba808SJacques Vidrine 	} else if (strcasecmp(cp, "STAT\r\n") == 0) {
1942b528cefcSMark Murray 		if (file_size != (off_t) -1)
1943b528cefcSMark Murray 			reply(213, "Status: %ld of %ld bytes transferred",
1944b528cefcSMark Murray 			      (long)byte_count,
1945b528cefcSMark Murray 			      (long)file_size);
1946b528cefcSMark Murray 		else
19478d4ba808SJacques Vidrine 			reply(213, "Status: %ld bytes transferred",
1948b528cefcSMark Murray 			      (long)byte_count);
1949b528cefcSMark Murray 	}
19508d4ba808SJacques Vidrine out:
19518d4ba808SJacques Vidrine 	return (transflag == 0);
1952b528cefcSMark Murray }
1953b528cefcSMark Murray 
1954b528cefcSMark Murray /*
1955b528cefcSMark Murray  * Note: a response of 425 is not mentioned as a possible response to
1956b528cefcSMark Murray  *	the PASV command in RFC959. However, it has been blessed as
1957b528cefcSMark Murray  *	a legitimate response by Jon Postel in a telephone conversation
1958b528cefcSMark Murray  *	with Rick Adams on 25 Jan 89.
1959b528cefcSMark Murray  */
1960b528cefcSMark Murray void
pasv(void)1961b528cefcSMark Murray pasv(void)
1962b528cefcSMark Murray {
19635e9cd1aeSAssar Westerlund 	socklen_t len;
1964b528cefcSMark Murray 	char *p, *a;
1965b528cefcSMark Murray 	struct sockaddr_in *sin;
1966b528cefcSMark Murray 
1967b528cefcSMark Murray 	if (ctrl_addr->sa_family != AF_INET) {
1968b528cefcSMark Murray 		reply(425,
1969b528cefcSMark Murray 		      "You cannot do PASV with something that's not IPv4");
1970b528cefcSMark Murray 		return;
1971b528cefcSMark Murray 	}
1972b528cefcSMark Murray 
19735e9cd1aeSAssar Westerlund 	if(pdata != -1)
19745e9cd1aeSAssar Westerlund 	    close(pdata);
19755e9cd1aeSAssar Westerlund 
1976b528cefcSMark Murray 	pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
1977b528cefcSMark Murray 	if (pdata < 0) {
1978b528cefcSMark Murray 		perror_reply(425, "Can't open passive connection");
1979b528cefcSMark Murray 		return;
1980b528cefcSMark Murray 	}
1981b528cefcSMark Murray 	pasv_addr->sa_family = ctrl_addr->sa_family;
1982b528cefcSMark Murray 	socket_set_address_and_port (pasv_addr,
1983b528cefcSMark Murray 				     socket_get_address (ctrl_addr),
1984b528cefcSMark Murray 				     0);
19854137ff4cSJacques Vidrine 	socket_set_portrange(pdata, restricted_data_ports,
19864137ff4cSJacques Vidrine 	    pasv_addr->sa_family);
1987c19800e8SDoug Rabson 	if (seteuid(0) < 0)
1988c19800e8SDoug Rabson 		fatal("Failed to seteuid");
1989b528cefcSMark Murray 	if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
1990c19800e8SDoug Rabson 		if (seteuid(pw->pw_uid) < 0)
1991c19800e8SDoug Rabson 			fatal("Failed to seteuid");
1992b528cefcSMark Murray 		goto pasv_error;
1993b528cefcSMark Murray 	}
1994c19800e8SDoug Rabson 	if (seteuid(pw->pw_uid) < 0)
1995c19800e8SDoug Rabson 		fatal("Failed to seteuid");
1996b528cefcSMark Murray 	len = sizeof(pasv_addr_ss);
1997b528cefcSMark Murray 	if (getsockname(pdata, pasv_addr, &len) < 0)
1998b528cefcSMark Murray 		goto pasv_error;
1999b528cefcSMark Murray 	if (listen(pdata, 1) < 0)
2000b528cefcSMark Murray 		goto pasv_error;
2001b528cefcSMark Murray 	sin = (struct sockaddr_in *)pasv_addr;
2002b528cefcSMark Murray 	a = (char *) &sin->sin_addr;
2003b528cefcSMark Murray 	p = (char *) &sin->sin_port;
2004b528cefcSMark Murray 
2005b528cefcSMark Murray #define UC(b) (((int) b) & 0xff)
2006b528cefcSMark Murray 
2007b528cefcSMark Murray 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
2008b528cefcSMark Murray 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
2009b528cefcSMark Murray 	return;
2010b528cefcSMark Murray 
2011b528cefcSMark Murray pasv_error:
2012b528cefcSMark Murray 	close(pdata);
2013b528cefcSMark Murray 	pdata = -1;
2014b528cefcSMark Murray 	perror_reply(425, "Can't open passive connection");
2015b528cefcSMark Murray 	return;
2016b528cefcSMark Murray }
2017b528cefcSMark Murray 
2018b528cefcSMark Murray void
epsv(char * proto)2019b528cefcSMark Murray epsv(char *proto)
2020b528cefcSMark Murray {
20215e9cd1aeSAssar Westerlund 	socklen_t len;
2022b528cefcSMark Murray 
2023b528cefcSMark Murray 	pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
2024b528cefcSMark Murray 	if (pdata < 0) {
2025b528cefcSMark Murray 		perror_reply(425, "Can't open passive connection");
2026b528cefcSMark Murray 		return;
2027b528cefcSMark Murray 	}
2028b528cefcSMark Murray 	pasv_addr->sa_family = ctrl_addr->sa_family;
2029b528cefcSMark Murray 	socket_set_address_and_port (pasv_addr,
2030b528cefcSMark Murray 				     socket_get_address (ctrl_addr),
2031b528cefcSMark Murray 				     0);
20324137ff4cSJacques Vidrine 	socket_set_portrange(pdata, restricted_data_ports,
20334137ff4cSJacques Vidrine 	    pasv_addr->sa_family);
2034c19800e8SDoug Rabson 	if (seteuid(0) < 0)
2035c19800e8SDoug Rabson 		fatal("Failed to seteuid");
2036b528cefcSMark Murray 	if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
2037c19800e8SDoug Rabson 		if (seteuid(pw->pw_uid))
2038c19800e8SDoug Rabson 			fatal("Failed to seteuid");
2039b528cefcSMark Murray 		goto pasv_error;
2040b528cefcSMark Murray 	}
2041c19800e8SDoug Rabson 	if (seteuid(pw->pw_uid) < 0)
2042c19800e8SDoug Rabson 		fatal("Failed to seteuid");
2043b528cefcSMark Murray 	len = sizeof(pasv_addr_ss);
2044b528cefcSMark Murray 	if (getsockname(pdata, pasv_addr, &len) < 0)
2045b528cefcSMark Murray 		goto pasv_error;
2046b528cefcSMark Murray 	if (listen(pdata, 1) < 0)
2047b528cefcSMark Murray 		goto pasv_error;
2048b528cefcSMark Murray 
2049b528cefcSMark Murray 	reply(229, "Entering Extended Passive Mode (|||%d|)",
2050b528cefcSMark Murray 	      ntohs(socket_get_port (pasv_addr)));
2051b528cefcSMark Murray 	return;
2052b528cefcSMark Murray 
2053b528cefcSMark Murray pasv_error:
2054b528cefcSMark Murray 	close(pdata);
2055b528cefcSMark Murray 	pdata = -1;
2056b528cefcSMark Murray 	perror_reply(425, "Can't open passive connection");
2057b528cefcSMark Murray 	return;
2058b528cefcSMark Murray }
2059b528cefcSMark Murray 
2060b528cefcSMark Murray void
eprt(char * str)2061b528cefcSMark Murray eprt(char *str)
2062b528cefcSMark Murray {
2063b528cefcSMark Murray 	char *end;
2064b528cefcSMark Murray 	char sep;
2065b528cefcSMark Murray 	int af;
2066b528cefcSMark Murray 	int ret;
2067b528cefcSMark Murray 	int port;
2068b528cefcSMark Murray 
2069b528cefcSMark Murray 	usedefault = 0;
2070b528cefcSMark Murray 	if (pdata >= 0) {
2071b528cefcSMark Murray 	    close(pdata);
2072b528cefcSMark Murray 	    pdata = -1;
2073b528cefcSMark Murray 	}
2074b528cefcSMark Murray 
2075b528cefcSMark Murray 	sep = *str++;
2076b528cefcSMark Murray 	if (sep == '\0') {
2077b528cefcSMark Murray 		reply(500, "Bad syntax in EPRT");
2078b528cefcSMark Murray 		return;
2079b528cefcSMark Murray 	}
2080b528cefcSMark Murray 	af = strtol (str, &end, 0);
2081b528cefcSMark Murray 	if (af == 0 || *end != sep) {
2082b528cefcSMark Murray 		reply(500, "Bad syntax in EPRT");
2083b528cefcSMark Murray 		return;
2084b528cefcSMark Murray 	}
2085b528cefcSMark Murray 	str = end + 1;
2086b528cefcSMark Murray 	switch (af) {
2087b528cefcSMark Murray #ifdef HAVE_IPV6
2088b528cefcSMark Murray 	case 2 :
2089b528cefcSMark Murray 	    data_dest->sa_family = AF_INET6;
2090b528cefcSMark Murray 	    break;
2091b528cefcSMark Murray #endif
2092b528cefcSMark Murray 	case 1 :
2093b528cefcSMark Murray 	    data_dest->sa_family = AF_INET;
2094b528cefcSMark Murray 		break;
2095b528cefcSMark Murray 	default :
2096b528cefcSMark Murray 		reply(522, "Network protocol %d not supported, use (1"
2097b528cefcSMark Murray #ifdef HAVE_IPV6
2098b528cefcSMark Murray 		      ",2"
2099b528cefcSMark Murray #endif
2100b528cefcSMark Murray 		      ")", af);
2101b528cefcSMark Murray 		return;
2102b528cefcSMark Murray 	}
2103b528cefcSMark Murray 	end = strchr (str, sep);
2104b528cefcSMark Murray 	if (end == NULL) {
2105b528cefcSMark Murray 		reply(500, "Bad syntax in EPRT");
2106b528cefcSMark Murray 		return;
2107b528cefcSMark Murray 	}
2108b528cefcSMark Murray 	*end = '\0';
2109b528cefcSMark Murray 	ret = inet_pton (data_dest->sa_family, str,
2110b528cefcSMark Murray 			 socket_get_address (data_dest));
2111b528cefcSMark Murray 
2112b528cefcSMark Murray 	if (ret != 1) {
2113b528cefcSMark Murray 		reply(500, "Bad address syntax in EPRT");
2114b528cefcSMark Murray 		return;
2115b528cefcSMark Murray 	}
2116b528cefcSMark Murray 	str = end + 1;
2117b528cefcSMark Murray 	port = strtol (str, &end, 0);
2118b528cefcSMark Murray 	if (port == 0 || *end != sep) {
2119b528cefcSMark Murray 		reply(500, "Bad port syntax in EPRT");
2120b528cefcSMark Murray 		return;
2121b528cefcSMark Murray 	}
2122*ae771770SStanislav Sedov 	if (port < IPPORT_RESERVED) {
2123*ae771770SStanislav Sedov 		reply(500, "Bad port in invalid range in EPRT");
2124*ae771770SStanislav Sedov 		return;
2125*ae771770SStanislav Sedov 	}
2126b528cefcSMark Murray 	socket_set_port (data_dest, htons(port));
2127*ae771770SStanislav Sedov 
2128*ae771770SStanislav Sedov 	if (paranoid &&
2129*ae771770SStanislav Sedov 	    (data_dest->sa_family != his_addr->sa_family ||
2130*ae771770SStanislav Sedov 	     memcmp(socket_get_address(data_dest), socket_get_address(his_addr), socket_sockaddr_size(data_dest)) != 0))
2131*ae771770SStanislav Sedov 	{
2132*ae771770SStanislav Sedov 		reply(500, "Bad address in EPRT");
2133*ae771770SStanislav Sedov 	}
2134b528cefcSMark Murray 	reply(200, "EPRT command successful.");
2135b528cefcSMark Murray }
2136b528cefcSMark Murray 
2137b528cefcSMark Murray /*
2138b528cefcSMark Murray  * Generate unique name for file with basename "local".
2139b528cefcSMark Murray  * The file named "local" is already known to exist.
2140b528cefcSMark Murray  * Generates failure reply on error.
2141b528cefcSMark Murray  */
2142b528cefcSMark Murray static char *
gunique(char * local)2143b528cefcSMark Murray gunique(char *local)
2144b528cefcSMark Murray {
2145b528cefcSMark Murray 	static char new[MaxPathLen];
2146b528cefcSMark Murray 	struct stat st;
2147b528cefcSMark Murray 	int count;
2148b528cefcSMark Murray 	char *cp;
2149b528cefcSMark Murray 
2150b528cefcSMark Murray 	cp = strrchr(local, '/');
2151b528cefcSMark Murray 	if (cp)
2152b528cefcSMark Murray 		*cp = '\0';
2153b528cefcSMark Murray 	if (stat(cp ? local : ".", &st) < 0) {
2154b528cefcSMark Murray 		perror_reply(553, cp ? local : ".");
2155b528cefcSMark Murray 		return NULL;
2156b528cefcSMark Murray 	}
2157b528cefcSMark Murray 	if (cp)
2158b528cefcSMark Murray 		*cp = '/';
2159b528cefcSMark Murray 	for (count = 1; count < 100; count++) {
2160b528cefcSMark Murray 		snprintf (new, sizeof(new), "%s.%d", local, count);
2161b528cefcSMark Murray 		if (stat(new, &st) < 0)
2162b528cefcSMark Murray 			return (new);
2163b528cefcSMark Murray 	}
2164b528cefcSMark Murray 	reply(452, "Unique file name cannot be created.");
2165b528cefcSMark Murray 	return (NULL);
2166b528cefcSMark Murray }
2167b528cefcSMark Murray 
2168b528cefcSMark Murray /*
2169b528cefcSMark Murray  * Format and send reply containing system error number.
2170b528cefcSMark Murray  */
2171b528cefcSMark Murray void
perror_reply(int code,const char * string)2172b528cefcSMark Murray perror_reply(int code, const char *string)
2173b528cefcSMark Murray {
2174b528cefcSMark Murray 	reply(code, "%s: %s.", string, strerror(errno));
2175b528cefcSMark Murray }
2176b528cefcSMark Murray 
2177b528cefcSMark Murray static char *onefile[] = {
2178b528cefcSMark Murray 	"",
2179b528cefcSMark Murray 	0
2180b528cefcSMark Murray };
2181b528cefcSMark Murray 
2182b528cefcSMark Murray void
list_file(char * file)2183b528cefcSMark Murray list_file(char *file)
2184b528cefcSMark Murray {
2185b528cefcSMark Murray     if(use_builtin_ls) {
2186b528cefcSMark Murray 	FILE *dout;
2187b528cefcSMark Murray 	dout = dataconn(file, -1, "w");
2188b528cefcSMark Murray 	if (dout == NULL)
2189b528cefcSMark Murray 	    return;
2190b528cefcSMark Murray 	set_buffer_size(fileno(dout), 0);
21918373020dSJacques Vidrine 	if(builtin_ls(dout, file) == 0)
2192b528cefcSMark Murray 	    reply(226, "Transfer complete.");
21938373020dSJacques Vidrine 	else
21948373020dSJacques Vidrine 	    reply(451, "Requested action aborted. Local error in processing.");
2195b528cefcSMark Murray 	fclose(dout);
2196b528cefcSMark Murray 	data = -1;
2197b528cefcSMark Murray 	pdata = -1;
2198b528cefcSMark Murray     } else {
2199b528cefcSMark Murray #ifdef HAVE_LS_A
22005e9cd1aeSAssar Westerlund 	const char *cmd = "/bin/ls -lA %s";
2201b528cefcSMark Murray #else
22025e9cd1aeSAssar Westerlund 	const char *cmd = "/bin/ls -la %s";
2203b528cefcSMark Murray #endif
2204b528cefcSMark Murray 	retrieve(cmd, file);
2205b528cefcSMark Murray     }
2206b528cefcSMark Murray }
2207b528cefcSMark Murray 
2208b528cefcSMark Murray void
send_file_list(char * whichf)2209b528cefcSMark Murray send_file_list(char *whichf)
2210b528cefcSMark Murray {
2211b528cefcSMark Murray     struct stat st;
2212b528cefcSMark Murray     DIR *dirp = NULL;
2213b528cefcSMark Murray     struct dirent *dir;
2214b528cefcSMark Murray     FILE *dout = NULL;
2215b528cefcSMark Murray     char **dirlist, *dirname;
2216b528cefcSMark Murray     int simple = 0;
2217b528cefcSMark Murray     int freeglob = 0;
2218b528cefcSMark Murray     glob_t gl;
2219b528cefcSMark Murray     char buf[MaxPathLen];
2220b528cefcSMark Murray 
2221b528cefcSMark Murray     if (strpbrk(whichf, "~{[*?") != NULL) {
22224137ff4cSJacques Vidrine 	int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|
22234137ff4cSJacques Vidrine #ifdef GLOB_MAXPATH
22244137ff4cSJacques Vidrine 	    GLOB_MAXPATH
22254137ff4cSJacques Vidrine #else
22264137ff4cSJacques Vidrine 	    GLOB_LIMIT
22274137ff4cSJacques Vidrine #endif
22284137ff4cSJacques Vidrine 	    ;
2229b528cefcSMark Murray 
2230b528cefcSMark Murray 	memset(&gl, 0, sizeof(gl));
2231b528cefcSMark Murray 	freeglob = 1;
2232b528cefcSMark Murray 	if (glob(whichf, flags, 0, &gl)) {
2233b528cefcSMark Murray 	    reply(550, "not found");
2234b528cefcSMark Murray 	    goto out;
2235b528cefcSMark Murray 	} else if (gl.gl_pathc == 0) {
2236b528cefcSMark Murray 	    errno = ENOENT;
2237b528cefcSMark Murray 	    perror_reply(550, whichf);
2238b528cefcSMark Murray 	    goto out;
2239b528cefcSMark Murray 	}
2240b528cefcSMark Murray 	dirlist = gl.gl_pathv;
2241b528cefcSMark Murray     } else {
2242b528cefcSMark Murray 	onefile[0] = whichf;
2243b528cefcSMark Murray 	dirlist = onefile;
2244b528cefcSMark Murray 	simple = 1;
2245b528cefcSMark Murray     }
2246b528cefcSMark Murray 
2247b528cefcSMark Murray     while ((dirname = *dirlist++)) {
22488d4ba808SJacques Vidrine 
22498d4ba808SJacques Vidrine 	if (urgflag && handleoobcmd())
22508d4ba808SJacques Vidrine 	    goto out;
22518d4ba808SJacques Vidrine 
2252b528cefcSMark Murray 	if (stat(dirname, &st) < 0) {
2253b528cefcSMark Murray 	    /*
2254b528cefcSMark Murray 	     * If user typed "ls -l", etc, and the client
2255b528cefcSMark Murray 	     * used NLST, do what the user meant.
2256b528cefcSMark Murray 	     */
2257b528cefcSMark Murray 	    if (dirname[0] == '-' && *dirlist == NULL &&
2258b528cefcSMark Murray 		transflag == 0) {
22595e9cd1aeSAssar Westerlund 		list_file(dirname);
2260b528cefcSMark Murray 		goto out;
2261b528cefcSMark Murray 	    }
2262b528cefcSMark Murray 	    perror_reply(550, whichf);
2263b528cefcSMark Murray 	    goto out;
2264b528cefcSMark Murray 	}
2265b528cefcSMark Murray 
2266b528cefcSMark Murray 	if (S_ISREG(st.st_mode)) {
2267b528cefcSMark Murray 	    if (dout == NULL) {
2268b528cefcSMark Murray 		dout = dataconn("file list", (off_t)-1, "w");
2269b528cefcSMark Murray 		if (dout == NULL)
2270b528cefcSMark Murray 		    goto out;
22718d4ba808SJacques Vidrine 		transflag = 1;
2272b528cefcSMark Murray 	    }
2273b528cefcSMark Murray 	    snprintf(buf, sizeof(buf), "%s%s\n", dirname,
2274b528cefcSMark Murray 		     type == TYPE_A ? "\r" : "");
2275b528cefcSMark Murray 	    sec_write(fileno(dout), buf, strlen(buf));
2276b528cefcSMark Murray 	    byte_count += strlen(dirname) + 1;
2277b528cefcSMark Murray 	    continue;
2278b528cefcSMark Murray 	} else if (!S_ISDIR(st.st_mode))
2279b528cefcSMark Murray 	    continue;
2280b528cefcSMark Murray 
2281b528cefcSMark Murray 	if ((dirp = opendir(dirname)) == NULL)
2282b528cefcSMark Murray 	    continue;
2283b528cefcSMark Murray 
2284b528cefcSMark Murray 	while ((dir = readdir(dirp)) != NULL) {
2285b528cefcSMark Murray 	    char nbuf[MaxPathLen];
2286b528cefcSMark Murray 
22878d4ba808SJacques Vidrine 	    if (urgflag && handleoobcmd())
22888d4ba808SJacques Vidrine 		goto out;
22898d4ba808SJacques Vidrine 
2290b528cefcSMark Murray 	    if (!strcmp(dir->d_name, "."))
2291b528cefcSMark Murray 		continue;
2292b528cefcSMark Murray 	    if (!strcmp(dir->d_name, ".."))
2293b528cefcSMark Murray 		continue;
2294b528cefcSMark Murray 
2295b528cefcSMark Murray 	    snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
2296b528cefcSMark Murray 
2297b528cefcSMark Murray 	    /*
2298b528cefcSMark Murray 	     * We have to do a stat to insure it's
2299b528cefcSMark Murray 	     * not a directory or special file.
2300b528cefcSMark Murray 	     */
2301b528cefcSMark Murray 	    if (simple || (stat(nbuf, &st) == 0 &&
2302b528cefcSMark Murray 			   S_ISREG(st.st_mode))) {
2303b528cefcSMark Murray 		if (dout == NULL) {
2304b528cefcSMark Murray 		    dout = dataconn("file list", (off_t)-1, "w");
2305b528cefcSMark Murray 		    if (dout == NULL)
2306b528cefcSMark Murray 			goto out;
23078d4ba808SJacques Vidrine 		    transflag = 1;
2308b528cefcSMark Murray 		}
2309b528cefcSMark Murray 		if(strncmp(nbuf, "./", 2) == 0)
2310b528cefcSMark Murray 		    snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2,
2311b528cefcSMark Murray 			     type == TYPE_A ? "\r" : "");
2312b528cefcSMark Murray 		else
2313b528cefcSMark Murray 		    snprintf(buf, sizeof(buf), "%s%s\n", nbuf,
2314b528cefcSMark Murray 			     type == TYPE_A ? "\r" : "");
2315b528cefcSMark Murray 		sec_write(fileno(dout), buf, strlen(buf));
2316b528cefcSMark Murray 		byte_count += strlen(nbuf) + 1;
2317b528cefcSMark Murray 	    }
2318b528cefcSMark Murray 	}
2319b528cefcSMark Murray 	closedir(dirp);
2320b528cefcSMark Murray     }
2321b528cefcSMark Murray     if (dout == NULL)
2322b528cefcSMark Murray 	reply(550, "No files found.");
2323b528cefcSMark Murray     else if (ferror(dout) != 0)
2324b528cefcSMark Murray 	perror_reply(550, "Data connection");
2325b528cefcSMark Murray     else
2326b528cefcSMark Murray 	reply(226, "Transfer complete.");
2327b528cefcSMark Murray 
23288d4ba808SJacques Vidrine out:
2329b528cefcSMark Murray     transflag = 0;
2330b528cefcSMark Murray     if (dout != NULL){
2331b528cefcSMark Murray 	sec_write(fileno(dout), buf, 0); /* XXX flush */
2332b528cefcSMark Murray 
2333b528cefcSMark Murray 	fclose(dout);
2334b528cefcSMark Murray     }
2335b528cefcSMark Murray     data = -1;
2336b528cefcSMark Murray     pdata = -1;
2337*ae771770SStanislav Sedov     if (freeglob)
2338b528cefcSMark Murray 	globfree(&gl);
2339b528cefcSMark Murray }
2340b528cefcSMark Murray 
2341b528cefcSMark Murray 
2342b528cefcSMark Murray int
find(char * pattern)2343b528cefcSMark Murray find(char *pattern)
2344b528cefcSMark Murray {
2345b528cefcSMark Murray     char line[1024];
2346b528cefcSMark Murray     FILE *f;
2347b528cefcSMark Murray 
2348b528cefcSMark Murray     snprintf(line, sizeof(line),
2349b528cefcSMark Murray 	     "/bin/locate -d %s -- %s",
2350b528cefcSMark Murray 	     ftp_rooted("/etc/locatedb"),
2351b528cefcSMark Murray 	     pattern);
2352b528cefcSMark Murray     f = ftpd_popen(line, "r", 1, 1);
2353b528cefcSMark Murray     if(f == NULL){
2354b528cefcSMark Murray 	perror_reply(550, "/bin/locate");
2355b528cefcSMark Murray 	return 1;
2356b528cefcSMark Murray     }
2357b528cefcSMark Murray     lreply(200, "Output from find.");
2358b528cefcSMark Murray     while(fgets(line, sizeof(line), f)){
2359b528cefcSMark Murray 	if(line[strlen(line)-1] == '\n')
2360b528cefcSMark Murray 	    line[strlen(line)-1] = 0;
2361b528cefcSMark Murray 	nreply("%s", line);
2362b528cefcSMark Murray     }
2363b528cefcSMark Murray     reply(200, "Done");
2364b528cefcSMark Murray     ftpd_pclose(f);
2365b528cefcSMark Murray     return 0;
2366b528cefcSMark Murray }
2367b528cefcSMark Murray 
2368