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