Deleted Added
full compact
opieftpd.c (62877) opieftpd.c (92914)
1/* opieftpd.c: Main program for an FTP daemon.
2
3%%% portions-copyright-cmetz-96
1/* opieftpd.c: Main program for an FTP daemon.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15 History:
16
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15 History:
16
17 Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18 opiestrncpy(). Fix incorrect use of setproctitle().
17 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
18 done already (and conditionally) in opie_cfg.h.
19 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
20 Merged in a security fix to BSD-derived ftpds.
21 Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
22 Moved LS_COMMAND here.
23 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
24 Removed useless strings (I don't think that removing the
25 ucb copyright one is a problem -- please let me know if
26 I'm wrong). Changed default CMASK to 077. Removed random
27 comments. Use ANSI stdargs for reply/lreply if we can,
28 added stdargs version of reply/lreply. Don't declare the
29 tos variable unless IP_TOS defined. Include stdargs headers
30 early. More headers ifdefed. Made everything static.
31 Got rid of gethostname() call and use of hostname. Pared
32 down status response for places where header files frequently
33 cause trouble. Made logging of user logins (ala -l)
34 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
35 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
36 result of opiechallenge (allows access control to work).
37 Added patches for AIX. Symbol changes for autoconf.
38 Modified at NRL for OPIE 2.01. Changed password lookup handling
39 to avoid problems with drain-bamaged shadow password packages.
40 Properly handle internal state for anonymous FTP. Unlock
41 user accounts properly if login fails because of /etc/shells.
42 Make sure to close syslog by function to avoid problems with
43 drain bamaged syslog implementations.
44 Modified at NRL for OPIE 2.0.
45 Originally from BSD Net/2.
46
47 There is some really, really ugly code in here.
48
19 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20 done already (and conditionally) in opie_cfg.h.
21 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22 Merged in a security fix to BSD-derived ftpds.
23 Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24 Moved LS_COMMAND here.
25 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26 Removed useless strings (I don't think that removing the
27 ucb copyright one is a problem -- please let me know if
28 I'm wrong). Changed default CMASK to 077. Removed random
29 comments. Use ANSI stdargs for reply/lreply if we can,
30 added stdargs version of reply/lreply. Don't declare the
31 tos variable unless IP_TOS defined. Include stdargs headers
32 early. More headers ifdefed. Made everything static.
33 Got rid of gethostname() call and use of hostname. Pared
34 down status response for places where header files frequently
35 cause trouble. Made logging of user logins (ala -l)
36 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38 result of opiechallenge (allows access control to work).
39 Added patches for AIX. Symbol changes for autoconf.
40 Modified at NRL for OPIE 2.01. Changed password lookup handling
41 to avoid problems with drain-bamaged shadow password packages.
42 Properly handle internal state for anonymous FTP. Unlock
43 user accounts properly if login fails because of /etc/shells.
44 Make sure to close syslog by function to avoid problems with
45 drain bamaged syslog implementations.
46 Modified at NRL for OPIE 2.0.
47 Originally from BSD Net/2.
48
49 There is some really, really ugly code in here.
50
49$FreeBSD: head/contrib/opie/opieftpd.c 62877 2000-07-10 07:30:28Z kris $
51$FreeBSD: head/contrib/opie/opieftpd.c 92914 2002-03-21 23:42:52Z markm $
50*/
51/*
52 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
53 * All rights reserved.
54 *
55 * Redistribution and use in source and binary forms, with or without
56 * modification, are permitted provided that the following conditions
57 * are met:
58 * 1. Redistributions of source code must retain the above copyright
59 * notice, this list of conditions and the following disclaimer.
60 * 2. Redistributions in binary form must reproduce the above copyright
61 * notice, this list of conditions and the following disclaimer in the
62 * documentation and/or other materials provided with the distribution.
63 * 3. All advertising materials mentioning features or use of this software
64 * must display the following acknowledgement:
65 * This product includes software developed by the University of
66 * California, Berkeley and its contributors.
67 * 4. Neither the name of the University nor the names of its contributors
68 * may be used to endorse or promote products derived from this software
69 * without specific prior written permission.
70 *
71 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
72 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
73 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
74 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
75 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
76 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
77 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
78 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
79 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
80 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
81 * SUCH DAMAGE.
82 */
83
84#include "opie_cfg.h"
85
86#if HAVE_ANSISTDARG
87#include <stdarg.h>
88#endif /* HAVE_ANSISTDARG */
89
90/*
91 * FTP server.
92 */
93
94#if HAVE_SYS_PARAM_H
95#include <sys/param.h>
96#endif /* HAVE_SYS_PARAM_H */
97#include <sys/stat.h>
98/* #include <sys/ioctl.h> */
99#include <sys/socket.h>
100#include <sys/wait.h>
101#ifdef SYS_FCNTL_H
102#include <sys/fcntl.h>
103#else
104#include <fcntl.h>
105#endif /* SYS_FCNTL_H */
106#include <sys/types.h>
107
108#include <netinet/in.h>
109#include <netinet/in_systm.h>
110#include <netinet/ip.h>
111
112#define FTP_NAMES
113#include <arpa/ftp.h>
114#include <arpa/inet.h>
115#include <arpa/telnet.h>
116
117#include <signal.h>
118#include <fcntl.h>
119#if HAVE_TIME_H
120#include <time.h>
121#endif /* HAVE_TIME_H */
122#if HAVE_PWD_H
123#include <pwd.h>
124#endif /* HAVE_PWD_H */
125#include <setjmp.h>
126#include <netdb.h>
127#include <errno.h>
128#include <syslog.h>
129#if HAVE_UNISTD_H
130#include <unistd.h>
131#endif /* HAVE_UNISTD_H */
132#include <stdio.h>
133#include <ctype.h>
134#include <stdlib.h>
135#include <string.h>
136#include <grp.h>
137
138#include "opie.h"
139
140#if HAVE_SHADOW_H
141#include <shadow.h>
142#endif /* HAVE_SHADOW_H */
143
144#if HAVE_CRYPT_H
145#include <crypt.h>
146#endif /* HAVE_CRYPT_H */
147
148#if HAVE_SYS_UTSNAME_H
149#include <sys/utsname.h>
150#endif /* HAVE_SYS_UTSNAME_H */
151
152#ifdef _AIX
153#include <sys/id.h>
154#include <sys/priv.h>
155#endif /* _AIX */
156
157#ifdef IP_TOS
158#ifndef IPTOS_THROUGHPUT
159#undef IP_TOS
160#endif /* !IPTOS_THROUGHPUT */
161#ifndef IPTOS_LOWDELAY
162#undef IP_TOS
163#endif /* !IPTOS_LOWDELAY */
164#endif /* IP_TOS */
165
166extern int errno;
167extern char *home; /* pointer to home directory for glob */
168extern FILE *ftpd_popen __P((char *, char *));
169extern int ftpd_pclose __P((FILE *));
170extern char cbuf[];
171extern off_t restart_point;
172
173static struct sockaddr_in ctrl_addr;
174static struct sockaddr_in data_source;
175struct sockaddr_in data_dest;
176struct sockaddr_in his_addr;
177static struct sockaddr_in pasv_addr;
178
179static int data;
180jmp_buf errcatch;
181static jmp_buf urgcatch;
182int logged_in;
183struct passwd *pw;
184int debug;
185int timeout = 900; /* timeout after 15 minutes of inactivity */
186int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */
187
188#if DOANONYMOUS
189static int guest;
190#endif /* DOANONYMOUS */
191int type;
192int form;
193static int stru; /* avoid C keyword */
194static int mode;
195int usedefault = 1; /* for data transfers */
196int pdata = -1; /* for passive mode */
197static int transflag;
198static off_t file_size;
199static off_t byte_count;
200
201#if (!defined(CMASK) || CMASK == 0)
202#undef CMASK
203#define CMASK 077
204#endif
205
206static int defumask = CMASK; /* default umask value */
207char tmpline[7];
208char remotehost[MAXHOSTNAMELEN];
209
210/*
211 * Timeout intervals for retrying connections
212 * to hosts that don't accept PORT cmds. This
213 * is a kludge, but given the problems with TCP...
214 */
215#define SWAITMAX 90 /* wait at most 90 seconds */
216#define SWAITINT 5 /* interval between retries */
217
218static int swaitmax = SWAITMAX;
219static int swaitint = SWAITINT;
220
221#if DOTITLE
222static char **Argv = NULL; /* pointer to argument vector */
223static char *LastArgv = NULL; /* end of argv */
224static char proctitle[BUFSIZ]; /* initial part of title */
225#endif /* DOTITLE */
226
227static int af_pwok = 0, pwok = 0;
228static struct opie opiestate;
229
230VOIDRET perror_reply __P((int, char *));
231VOIDRET dologout __P((int));
232char *getline __P((char *, int, FILE *));
233VOIDRET upper __P((char *));
234
235static VOIDRET lostconn __P((int));
236static VOIDRET myoob __P((int));
237static FILE *getdatasock __P((char *));
238static FILE *dataconn __P((char *, off_t, char *));
239static int checkuser __P((char *));
240static VOIDRET end_login __P((void));
241static VOIDRET send_data __P((FILE *, FILE *, off_t));
242static int receive_data __P((FILE *, FILE *));
243static char *gunique __P((char *));
244static char *sgetsave __P((char *));
245
52*/
53/*
54 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55 * All rights reserved.
56 *
57 * Redistribution and use in source and binary forms, with or without
58 * modification, are permitted provided that the following conditions
59 * are met:
60 * 1. Redistributions of source code must retain the above copyright
61 * notice, this list of conditions and the following disclaimer.
62 * 2. Redistributions in binary form must reproduce the above copyright
63 * notice, this list of conditions and the following disclaimer in the
64 * documentation and/or other materials provided with the distribution.
65 * 3. All advertising materials mentioning features or use of this software
66 * must display the following acknowledgement:
67 * This product includes software developed by the University of
68 * California, Berkeley and its contributors.
69 * 4. Neither the name of the University nor the names of its contributors
70 * may be used to endorse or promote products derived from this software
71 * without specific prior written permission.
72 *
73 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83 * SUCH DAMAGE.
84 */
85
86#include "opie_cfg.h"
87
88#if HAVE_ANSISTDARG
89#include <stdarg.h>
90#endif /* HAVE_ANSISTDARG */
91
92/*
93 * FTP server.
94 */
95
96#if HAVE_SYS_PARAM_H
97#include <sys/param.h>
98#endif /* HAVE_SYS_PARAM_H */
99#include <sys/stat.h>
100/* #include <sys/ioctl.h> */
101#include <sys/socket.h>
102#include <sys/wait.h>
103#ifdef SYS_FCNTL_H
104#include <sys/fcntl.h>
105#else
106#include <fcntl.h>
107#endif /* SYS_FCNTL_H */
108#include <sys/types.h>
109
110#include <netinet/in.h>
111#include <netinet/in_systm.h>
112#include <netinet/ip.h>
113
114#define FTP_NAMES
115#include <arpa/ftp.h>
116#include <arpa/inet.h>
117#include <arpa/telnet.h>
118
119#include <signal.h>
120#include <fcntl.h>
121#if HAVE_TIME_H
122#include <time.h>
123#endif /* HAVE_TIME_H */
124#if HAVE_PWD_H
125#include <pwd.h>
126#endif /* HAVE_PWD_H */
127#include <setjmp.h>
128#include <netdb.h>
129#include <errno.h>
130#include <syslog.h>
131#if HAVE_UNISTD_H
132#include <unistd.h>
133#endif /* HAVE_UNISTD_H */
134#include <stdio.h>
135#include <ctype.h>
136#include <stdlib.h>
137#include <string.h>
138#include <grp.h>
139
140#include "opie.h"
141
142#if HAVE_SHADOW_H
143#include <shadow.h>
144#endif /* HAVE_SHADOW_H */
145
146#if HAVE_CRYPT_H
147#include <crypt.h>
148#endif /* HAVE_CRYPT_H */
149
150#if HAVE_SYS_UTSNAME_H
151#include <sys/utsname.h>
152#endif /* HAVE_SYS_UTSNAME_H */
153
154#ifdef _AIX
155#include <sys/id.h>
156#include <sys/priv.h>
157#endif /* _AIX */
158
159#ifdef IP_TOS
160#ifndef IPTOS_THROUGHPUT
161#undef IP_TOS
162#endif /* !IPTOS_THROUGHPUT */
163#ifndef IPTOS_LOWDELAY
164#undef IP_TOS
165#endif /* !IPTOS_LOWDELAY */
166#endif /* IP_TOS */
167
168extern int errno;
169extern char *home; /* pointer to home directory for glob */
170extern FILE *ftpd_popen __P((char *, char *));
171extern int ftpd_pclose __P((FILE *));
172extern char cbuf[];
173extern off_t restart_point;
174
175static struct sockaddr_in ctrl_addr;
176static struct sockaddr_in data_source;
177struct sockaddr_in data_dest;
178struct sockaddr_in his_addr;
179static struct sockaddr_in pasv_addr;
180
181static int data;
182jmp_buf errcatch;
183static jmp_buf urgcatch;
184int logged_in;
185struct passwd *pw;
186int debug;
187int timeout = 900; /* timeout after 15 minutes of inactivity */
188int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */
189
190#if DOANONYMOUS
191static int guest;
192#endif /* DOANONYMOUS */
193int type;
194int form;
195static int stru; /* avoid C keyword */
196static int mode;
197int usedefault = 1; /* for data transfers */
198int pdata = -1; /* for passive mode */
199static int transflag;
200static off_t file_size;
201static off_t byte_count;
202
203#if (!defined(CMASK) || CMASK == 0)
204#undef CMASK
205#define CMASK 077
206#endif
207
208static int defumask = CMASK; /* default umask value */
209char tmpline[7];
210char remotehost[MAXHOSTNAMELEN];
211
212/*
213 * Timeout intervals for retrying connections
214 * to hosts that don't accept PORT cmds. This
215 * is a kludge, but given the problems with TCP...
216 */
217#define SWAITMAX 90 /* wait at most 90 seconds */
218#define SWAITINT 5 /* interval between retries */
219
220static int swaitmax = SWAITMAX;
221static int swaitint = SWAITINT;
222
223#if DOTITLE
224static char **Argv = NULL; /* pointer to argument vector */
225static char *LastArgv = NULL; /* end of argv */
226static char proctitle[BUFSIZ]; /* initial part of title */
227#endif /* DOTITLE */
228
229static int af_pwok = 0, pwok = 0;
230static struct opie opiestate;
231
232VOIDRET perror_reply __P((int, char *));
233VOIDRET dologout __P((int));
234char *getline __P((char *, int, FILE *));
235VOIDRET upper __P((char *));
236
237static VOIDRET lostconn __P((int));
238static VOIDRET myoob __P((int));
239static FILE *getdatasock __P((char *));
240static FILE *dataconn __P((char *, off_t, char *));
241static int checkuser __P((char *));
242static VOIDRET end_login __P((void));
243static VOIDRET send_data __P((FILE *, FILE *, off_t));
244static int receive_data __P((FILE *, FILE *));
245static char *gunique __P((char *));
246static char *sgetsave __P((char *));
247
246int opielogwtmp __P((char *, char *, char *));
248int opielogwtmp __P((char *, char *, char *, char *));
247
248int fclose __P((FILE *));
249
250#ifdef HAVE_ANSISTDARG
251VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
252{
253 va_list ap;
254 char buffer[1024];
255
256 va_start(ap, fmt);
257 vsprintf(buffer, fmt, ap);
258 va_end(ap);
259
260 printf("%d %s\r\n", n, buffer);
261 fflush(stdout);
262 if (debug)
263 syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
264}
265#else /* HAVE_ANSISTDARG */
266VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
267{
268 printf("%d ", n);
269 printf(fmt, p0, p1, p2, p3, p4, p5);
270 printf("\r\n");
271 fflush(stdout);
272 if (debug) {
273 syslog(LOG_DEBUG, "<--- %d ", n);
274 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
275 }
276}
277#endif /* HAVE_ANSISTDARG */
278
279#ifdef HAVE_ANSISTDARG
280VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
281{
282 va_list ap;
283 char buffer[1024];
284
285 va_start(ap, fmt);
286 vsprintf(buffer, fmt, ap);
287 va_end(ap);
288
289 printf("%d- %s\r\n", n, buffer);
290 fflush(stdout);
291 if (debug)
292 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
293}
294#else /* HAVE_ANSISTDARG */
295VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
296{
297 printf("%d- ", n);
298 printf(fmt, p0, p1, p2, p3, p4, p5);
299 printf("\r\n");
300 fflush(stdout);
301 if (debug) {
302 syslog(LOG_DEBUG, "<--- %d- ", n);
303 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
304 }
305}
306#endif /* HAVE_ANSISTDARG */
307
308VOIDRET enable_signalling FUNCTION_NOARGS
309{
310 signal(SIGPIPE, lostconn);
311 if ((int)signal(SIGURG, myoob) < 0)
312 syslog(LOG_ERR, "signal: %m");
313}
314
315VOIDRET disable_signalling FUNCTION_NOARGS
316{
317 signal(SIGPIPE, SIG_IGN);
318 if ((int)signal(SIGURG, SIG_IGN) < 0)
319 syslog(LOG_ERR, "signal: %m");
320}
321
322static VOIDRET lostconn FUNCTION((input), int input)
323{
324 if (debug)
325 syslog(LOG_DEBUG, "lost connection");
326 dologout(-1);
327}
328
329static char ttyline[20];
330
331/*
332 * Helper function for sgetpwnam().
333 */
334static char *sgetsave FUNCTION((s), char *s)
335{
336 char *new = malloc((unsigned) strlen(s) + 1);
337
338 if (new == NULL) {
339 perror_reply(421, "Local resource failure: malloc");
340 dologout(1);
341 /* NOTREACHED */
342 }
343 strcpy(new, s);
344 return (new);
345}
346
347/*
348 * Save the result of a getpwnam. Used for USER command, since
349 * the data returned must not be clobbered by any other command
350 * (e.g., globbing).
351 */
352static struct passwd *sgetpwnam FUNCTION((name), char *name)
353{
354 static struct passwd save;
355 register struct passwd *p;
356
357#if HAVE_SHADOW
358 struct spwd *spwd;
359#endif /* HAVE_SHADOW */
360
361 if ((p = getpwnam(name)) == NULL)
362 return (p);
363
364#if HAVE_SHADOW
365 if ((spwd = getspnam(name)) == NULL)
366 return NULL;
367
368 endspent();
369
370 p->pw_passwd = spwd->sp_pwdp;
371#endif /* HAVE_SHADOW */
372
373 endpwent();
374
375 if (save.pw_name) {
376 free(save.pw_name);
377 free(save.pw_passwd);
378 free(save.pw_gecos);
379 free(save.pw_dir);
380 free(save.pw_shell);
381 }
382 save = *p;
383 save.pw_name = sgetsave(p->pw_name);
384 save.pw_passwd = sgetsave(p->pw_passwd);
385 save.pw_gecos = sgetsave(p->pw_gecos);
386 save.pw_dir = sgetsave(p->pw_dir);
387 save.pw_shell = sgetsave(p->pw_shell);
388 return (&save);
389}
390
391int login_attempts; /* number of failed login attempts */
392int askpasswd; /* had user command, ask for passwd */
393
394/*
395 * USER command.
396 * Sets global passwd pointer pw if named account exists and is acceptable;
397 * sets askpasswd if a PASS command is expected. If logged in previously,
398 * need to reset state. If name is "ftp" or "anonymous", the name is not in
399 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
400 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
401 * requesting login privileges. Disallow anyone who does not have a standard
402 * shell as returned by getusershell(). Disallow anyone mentioned in the file
403 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
404 */
405int user FUNCTION((name), char *name)
406{
407 register char *cp;
408 char *shell;
409
410 if (logged_in) {
411#if DOANONYMOUS
412 if (guest) {
413 reply(530, "Can't change user from guest login.");
414 return -1;
415 }
416#endif /* DOANONMOUS */
417 end_login();
418 }
419 askpasswd = 1;
420#if DOANONYMOUS
421 guest = 0;
422 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
423 if (!checkuser("ftp") && !checkuser("anonymous"))
424 if ((pw = sgetpwnam("ftp")) != NULL) {
425 guest = 1;
426 askpasswd = 1;
427 reply(331, "Guest login ok, send your e-mail address as your password.");
428 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
429 return 0;
430 }
431#endif /* DOANONYMOUS */
432 if (pw = sgetpwnam(name)) {
433 if ((shell = pw->pw_shell) == NULL || *shell == 0)
434 shell = _PATH_BSHELL;
435 while ((cp = getusershell()) != NULL)
436 if (!strcmp(cp, shell))
437 break;
438 endusershell();
439 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
440#if DEBUG
441 if (!cp)
442 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
443 if (checkuser(name))
444 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
445 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
446 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
447#endif /* DEBUG */
448 pw = (struct passwd *) NULL;
449 askpasswd = -1;
450 }
451 }
452 {
453 char prompt[OPIE_CHALLENGE_MAX + 1];
454
455 opiechallenge(&opiestate, name, prompt);
456
457 if (askpasswd == -1) {
458 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
459 pwok = 0;
460 } else
461 pwok = af_pwok && opiealways(pw->pw_dir);
462
463#if NEW_PROMPTS
464 reply(331, "Response to %s %s for %s.", prompt,
465#else /* NEW_PROMPTS */
466 reply(331, "OTP response %s %s for %s.", prompt,
467#endif /* NEW_PROMPTS */
468 pwok ? "requested" : "required", name);
469 }
470 /* Delay before reading passwd after first failed attempt to slow down
471 passwd-guessing programs. */
472 if (login_attempts)
473 sleep((unsigned) login_attempts);
474
475 return 0;
476}
477
478/*
479 * Check if a user is in the file _PATH_FTPUSERS
480 */
481static int checkuser FUNCTION((name), char *name)
482{
483 register FILE *fd;
484 register char *p;
485 char line[BUFSIZ];
486
487 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
488 while (fgets(line, sizeof(line), fd) != NULL)
489 if ((p = strchr(line, '\n')) != NULL) {
490 *p = '\0';
491 if (line[0] == '#')
492 continue;
493 if (!strcmp(line, name)) {
494 fclose(fd);
495 return (1);
496 }
497 }
498 fclose(fd);
499 }
500 return (0);
501}
502
503/*
504 * Terminate login as previous user, if any, resetting state;
505 * used when USER command is given or login fails.
506 */
507static VOIDRET end_login FUNCTION_NOARGS
508{
509 disable_signalling();
510 if (seteuid((uid_t) 0))
511 syslog(LOG_ERR, "Can't set euid");
512 if (logged_in)
249
250int fclose __P((FILE *));
251
252#ifdef HAVE_ANSISTDARG
253VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
254{
255 va_list ap;
256 char buffer[1024];
257
258 va_start(ap, fmt);
259 vsprintf(buffer, fmt, ap);
260 va_end(ap);
261
262 printf("%d %s\r\n", n, buffer);
263 fflush(stdout);
264 if (debug)
265 syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
266}
267#else /* HAVE_ANSISTDARG */
268VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
269{
270 printf("%d ", n);
271 printf(fmt, p0, p1, p2, p3, p4, p5);
272 printf("\r\n");
273 fflush(stdout);
274 if (debug) {
275 syslog(LOG_DEBUG, "<--- %d ", n);
276 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
277 }
278}
279#endif /* HAVE_ANSISTDARG */
280
281#ifdef HAVE_ANSISTDARG
282VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
283{
284 va_list ap;
285 char buffer[1024];
286
287 va_start(ap, fmt);
288 vsprintf(buffer, fmt, ap);
289 va_end(ap);
290
291 printf("%d- %s\r\n", n, buffer);
292 fflush(stdout);
293 if (debug)
294 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
295}
296#else /* HAVE_ANSISTDARG */
297VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
298{
299 printf("%d- ", n);
300 printf(fmt, p0, p1, p2, p3, p4, p5);
301 printf("\r\n");
302 fflush(stdout);
303 if (debug) {
304 syslog(LOG_DEBUG, "<--- %d- ", n);
305 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
306 }
307}
308#endif /* HAVE_ANSISTDARG */
309
310VOIDRET enable_signalling FUNCTION_NOARGS
311{
312 signal(SIGPIPE, lostconn);
313 if ((int)signal(SIGURG, myoob) < 0)
314 syslog(LOG_ERR, "signal: %m");
315}
316
317VOIDRET disable_signalling FUNCTION_NOARGS
318{
319 signal(SIGPIPE, SIG_IGN);
320 if ((int)signal(SIGURG, SIG_IGN) < 0)
321 syslog(LOG_ERR, "signal: %m");
322}
323
324static VOIDRET lostconn FUNCTION((input), int input)
325{
326 if (debug)
327 syslog(LOG_DEBUG, "lost connection");
328 dologout(-1);
329}
330
331static char ttyline[20];
332
333/*
334 * Helper function for sgetpwnam().
335 */
336static char *sgetsave FUNCTION((s), char *s)
337{
338 char *new = malloc((unsigned) strlen(s) + 1);
339
340 if (new == NULL) {
341 perror_reply(421, "Local resource failure: malloc");
342 dologout(1);
343 /* NOTREACHED */
344 }
345 strcpy(new, s);
346 return (new);
347}
348
349/*
350 * Save the result of a getpwnam. Used for USER command, since
351 * the data returned must not be clobbered by any other command
352 * (e.g., globbing).
353 */
354static struct passwd *sgetpwnam FUNCTION((name), char *name)
355{
356 static struct passwd save;
357 register struct passwd *p;
358
359#if HAVE_SHADOW
360 struct spwd *spwd;
361#endif /* HAVE_SHADOW */
362
363 if ((p = getpwnam(name)) == NULL)
364 return (p);
365
366#if HAVE_SHADOW
367 if ((spwd = getspnam(name)) == NULL)
368 return NULL;
369
370 endspent();
371
372 p->pw_passwd = spwd->sp_pwdp;
373#endif /* HAVE_SHADOW */
374
375 endpwent();
376
377 if (save.pw_name) {
378 free(save.pw_name);
379 free(save.pw_passwd);
380 free(save.pw_gecos);
381 free(save.pw_dir);
382 free(save.pw_shell);
383 }
384 save = *p;
385 save.pw_name = sgetsave(p->pw_name);
386 save.pw_passwd = sgetsave(p->pw_passwd);
387 save.pw_gecos = sgetsave(p->pw_gecos);
388 save.pw_dir = sgetsave(p->pw_dir);
389 save.pw_shell = sgetsave(p->pw_shell);
390 return (&save);
391}
392
393int login_attempts; /* number of failed login attempts */
394int askpasswd; /* had user command, ask for passwd */
395
396/*
397 * USER command.
398 * Sets global passwd pointer pw if named account exists and is acceptable;
399 * sets askpasswd if a PASS command is expected. If logged in previously,
400 * need to reset state. If name is "ftp" or "anonymous", the name is not in
401 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
403 * requesting login privileges. Disallow anyone who does not have a standard
404 * shell as returned by getusershell(). Disallow anyone mentioned in the file
405 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406 */
407int user FUNCTION((name), char *name)
408{
409 register char *cp;
410 char *shell;
411
412 if (logged_in) {
413#if DOANONYMOUS
414 if (guest) {
415 reply(530, "Can't change user from guest login.");
416 return -1;
417 }
418#endif /* DOANONMOUS */
419 end_login();
420 }
421 askpasswd = 1;
422#if DOANONYMOUS
423 guest = 0;
424 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
425 if (!checkuser("ftp") && !checkuser("anonymous"))
426 if ((pw = sgetpwnam("ftp")) != NULL) {
427 guest = 1;
428 askpasswd = 1;
429 reply(331, "Guest login ok, send your e-mail address as your password.");
430 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
431 return 0;
432 }
433#endif /* DOANONYMOUS */
434 if (pw = sgetpwnam(name)) {
435 if ((shell = pw->pw_shell) == NULL || *shell == 0)
436 shell = _PATH_BSHELL;
437 while ((cp = getusershell()) != NULL)
438 if (!strcmp(cp, shell))
439 break;
440 endusershell();
441 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
442#if DEBUG
443 if (!cp)
444 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
445 if (checkuser(name))
446 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
447 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
448 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
449#endif /* DEBUG */
450 pw = (struct passwd *) NULL;
451 askpasswd = -1;
452 }
453 }
454 {
455 char prompt[OPIE_CHALLENGE_MAX + 1];
456
457 opiechallenge(&opiestate, name, prompt);
458
459 if (askpasswd == -1) {
460 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
461 pwok = 0;
462 } else
463 pwok = af_pwok && opiealways(pw->pw_dir);
464
465#if NEW_PROMPTS
466 reply(331, "Response to %s %s for %s.", prompt,
467#else /* NEW_PROMPTS */
468 reply(331, "OTP response %s %s for %s.", prompt,
469#endif /* NEW_PROMPTS */
470 pwok ? "requested" : "required", name);
471 }
472 /* Delay before reading passwd after first failed attempt to slow down
473 passwd-guessing programs. */
474 if (login_attempts)
475 sleep((unsigned) login_attempts);
476
477 return 0;
478}
479
480/*
481 * Check if a user is in the file _PATH_FTPUSERS
482 */
483static int checkuser FUNCTION((name), char *name)
484{
485 register FILE *fd;
486 register char *p;
487 char line[BUFSIZ];
488
489 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
490 while (fgets(line, sizeof(line), fd) != NULL)
491 if ((p = strchr(line, '\n')) != NULL) {
492 *p = '\0';
493 if (line[0] == '#')
494 continue;
495 if (!strcmp(line, name)) {
496 fclose(fd);
497 return (1);
498 }
499 }
500 fclose(fd);
501 }
502 return (0);
503}
504
505/*
506 * Terminate login as previous user, if any, resetting state;
507 * used when USER command is given or login fails.
508 */
509static VOIDRET end_login FUNCTION_NOARGS
510{
511 disable_signalling();
512 if (seteuid((uid_t) 0))
513 syslog(LOG_ERR, "Can't set euid");
514 if (logged_in)
513 opielogwtmp(ttyline, "", "");
515 opielogwtmp(ttyline, "", "", "ftp");
514 pw = NULL;
515 logged_in = 0;
516#if DOANONYMOUS
517 guest = 0;
518#endif /* DOANONYMOUS */
519 enable_signalling();
520}
521
522VOIDRET pass FUNCTION((passwd), char *passwd)
523{
524 int legit = askpasswd + 1, i;
525
526 if (logged_in || askpasswd == 0) {
527 reply(503, "Login with USER first.");
528 return;
529 }
530 askpasswd = 0;
531
532#if DOANONYMOUS
533 if (!guest) { /* "ftp" is only account allowed no password */
534#endif /* DOANONYMOUS */
535 i = opieverify(&opiestate, passwd);
536 if (legit && i && pwok)
537 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
538 if (!legit || i) {
539 reply(530, "Login incorrect.");
540 pw = NULL;
541 if (login_attempts++ >= 5) {
542 syslog(LOG_WARNING,
543 "Repeated login failures for user %s from %s",
544 pw->pw_name, remotehost);
545 exit(0);
546 }
547 return;
548 }
549#if DOANONYMOUS
550 } else
551 if ((passwd[0] <= ' ') || checkuser(passwd)) {
552 reply(530, "No identity, no service.");
553 syslog(LOG_DEBUG, "Bogus address: %s", passwd);
554 exit(0);
555 }
556#endif /* DOANONYMOUS */
557 login_attempts = 0; /* this time successful */
558 if (setegid((gid_t) pw->pw_gid) < 0) {
559 reply(550, "Can't set gid.");
560 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
561 return;
562 }
563 initgroups(pw->pw_name, pw->pw_gid);
564
565 /* open wtmp before chroot */
566 sprintf(ttyline, "ftp%d", getpid());
516 pw = NULL;
517 logged_in = 0;
518#if DOANONYMOUS
519 guest = 0;
520#endif /* DOANONYMOUS */
521 enable_signalling();
522}
523
524VOIDRET pass FUNCTION((passwd), char *passwd)
525{
526 int legit = askpasswd + 1, i;
527
528 if (logged_in || askpasswd == 0) {
529 reply(503, "Login with USER first.");
530 return;
531 }
532 askpasswd = 0;
533
534#if DOANONYMOUS
535 if (!guest) { /* "ftp" is only account allowed no password */
536#endif /* DOANONYMOUS */
537 i = opieverify(&opiestate, passwd);
538 if (legit && i && pwok)
539 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
540 if (!legit || i) {
541 reply(530, "Login incorrect.");
542 pw = NULL;
543 if (login_attempts++ >= 5) {
544 syslog(LOG_WARNING,
545 "Repeated login failures for user %s from %s",
546 pw->pw_name, remotehost);
547 exit(0);
548 }
549 return;
550 }
551#if DOANONYMOUS
552 } else
553 if ((passwd[0] <= ' ') || checkuser(passwd)) {
554 reply(530, "No identity, no service.");
555 syslog(LOG_DEBUG, "Bogus address: %s", passwd);
556 exit(0);
557 }
558#endif /* DOANONYMOUS */
559 login_attempts = 0; /* this time successful */
560 if (setegid((gid_t) pw->pw_gid) < 0) {
561 reply(550, "Can't set gid.");
562 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
563 return;
564 }
565 initgroups(pw->pw_name, pw->pw_gid);
566
567 /* open wtmp before chroot */
568 sprintf(ttyline, "ftp%d", getpid());
567 opielogwtmp(ttyline, pw->pw_name, remotehost);
569 opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
568 logged_in = 1;
569
570#if DOANONYMOUS
571 if (guest) {
572 /* We MUST do a chdir() after the chroot. Otherwise the old current
573 directory will be accessible as "." outside the new root! */
574 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
575 reply(550, "Can't set guest privileges.");
576 goto bad;
577 }
578 } else
579#endif /* DOANONYMOUS */
580 if (chdir(pw->pw_dir) < 0) {
581 if (chdir("/") < 0) {
582 reply(530, "User %s: can't change directory to %s.",
583 pw->pw_name, pw->pw_dir);
584 goto bad;
585 } else
586 lreply(230, "No directory! Logging in with home=/");
587 }
588/* This patch was contributed by an OPIE user. We don't know what it
589 does, exactly. It may or may not work. */
590#ifdef _AIX
591 {
592 priv_t priv;
593 priv.pv_priv[0] = 0;
594 priv.pv_priv[1] = 0;
595 setgroups(NULL, NULL);
596 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
597 &priv, sizeof(priv_t)) < 0 ||
598 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
599 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
600 seteuid((uid_t)pw->pw_uid) < 0) {
601 reply(550, "Can't set uid (_AIX3).");
602 goto bad;
603 }
604 }
605#else /* _AIX */
606 if (seteuid((uid_t) pw->pw_uid) < 0) {
607 reply(550, "Can't set uid.");
608 goto bad;
609 }
610#endif /* _AIX */
611 /*
612 * Display a login message, if it exists.
613 * N.B. reply(230,) must follow the message.
614 */
615 {
616 FILE *fd;
617
618 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
619 char *cp, line[128];
620
621 while (fgets(line, sizeof(line), fd) != NULL) {
622 if ((cp = strchr(line, '\n')) != NULL)
623 *cp = '\0';
624 lreply(230, "%s", line);
625 }
626 (void) fflush(stdout);
627 (void) fclose(fd);
628 }
629 }
630#if DOANONYMOUS
631 if (guest) {
632 reply(230, "Guest login ok, access restrictions apply.");
633#if DOTITLE
570 logged_in = 1;
571
572#if DOANONYMOUS
573 if (guest) {
574 /* We MUST do a chdir() after the chroot. Otherwise the old current
575 directory will be accessible as "." outside the new root! */
576 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
577 reply(550, "Can't set guest privileges.");
578 goto bad;
579 }
580 } else
581#endif /* DOANONYMOUS */
582 if (chdir(pw->pw_dir) < 0) {
583 if (chdir("/") < 0) {
584 reply(530, "User %s: can't change directory to %s.",
585 pw->pw_name, pw->pw_dir);
586 goto bad;
587 } else
588 lreply(230, "No directory! Logging in with home=/");
589 }
590/* This patch was contributed by an OPIE user. We don't know what it
591 does, exactly. It may or may not work. */
592#ifdef _AIX
593 {
594 priv_t priv;
595 priv.pv_priv[0] = 0;
596 priv.pv_priv[1] = 0;
597 setgroups(NULL, NULL);
598 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
599 &priv, sizeof(priv_t)) < 0 ||
600 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
601 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
602 seteuid((uid_t)pw->pw_uid) < 0) {
603 reply(550, "Can't set uid (_AIX3).");
604 goto bad;
605 }
606 }
607#else /* _AIX */
608 if (seteuid((uid_t) pw->pw_uid) < 0) {
609 reply(550, "Can't set uid.");
610 goto bad;
611 }
612#endif /* _AIX */
613 /*
614 * Display a login message, if it exists.
615 * N.B. reply(230,) must follow the message.
616 */
617 {
618 FILE *fd;
619
620 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
621 char *cp, line[128];
622
623 while (fgets(line, sizeof(line), fd) != NULL) {
624 if ((cp = strchr(line, '\n')) != NULL)
625 *cp = '\0';
626 lreply(230, "%s", line);
627 }
628 (void) fflush(stdout);
629 (void) fclose(fd);
630 }
631 }
632#if DOANONYMOUS
633 if (guest) {
634 reply(230, "Guest login ok, access restrictions apply.");
635#if DOTITLE
634 snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%s", remotehost,
635 passwd);
636 setproctitle("%s", proctitle);
637#endif /* DOTITLE */
636 setproctitle("%s: anonymous/%.*s", remotehost,
637 sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
638 passwd);
639#endif /* DOTITLE */
638 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
639 remotehost, passwd);
640 } else
641#endif /* DOANONYMOUS */
642 {
643 reply(230, "User %s logged in.", pw->pw_name);
644
645#if DOTITLE
640 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
641 remotehost, passwd);
642 } else
643#endif /* DOANONYMOUS */
644 {
645 reply(230, "User %s logged in.", pw->pw_name);
646
647#if DOTITLE
646 snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
647 setproctitle("%s", proctitle);
648#endif /* DOTITLE */
648 setproctitle("%s: %s", remotehost, pw->pw_name);
649#endif /* DOTITLE */
649 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
650 }
651 home = pw->pw_dir; /* home dir for globbing */
652 umask(defumask);
653 return;
654
655bad:
656 /* Forget all about it... */
657 end_login();
658}
659
660VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
661{
662 FILE *fin, *dout;
663 struct stat st;
664 int (*closefunc) ();
665
666 if (cmd == 0) {
667 fin = fopen(name, "r"), closefunc = fclose;
668 st.st_size = 0;
669 } else {
670 char line[BUFSIZ];
671
672 snprintf(line, sizeof(line), cmd, name);
673 name = line;
674 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
675 st.st_size = -1;
676#if HAVE_ST_BLKSIZE
677 st.st_blksize = BUFSIZ;
678#endif /* HAVE_ST_BLKSIZE */
679 }
680 if (fin == NULL) {
681 if (errno != 0)
682 perror_reply(550, name);
683 return;
684 }
685 if (cmd == 0 &&
686 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
687 reply(550, "%s: not a plain file.", name);
688 goto done;
689 }
690 if (restart_point) {
691 if (type == TYPE_A) {
692 register int i, n, c;
693
694 n = restart_point;
695 i = 0;
696 while (i++ < n) {
697 if ((c = getc(fin)) == EOF) {
698 perror_reply(550, name);
699 goto done;
700 }
701 if (c == '\n')
702 i++;
703 }
704 } else
705 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
706 perror_reply(550, name);
707 goto done;
708 }
709 }
710 dout = dataconn(name, st.st_size, "w");
711 if (dout == NULL)
712 goto done;
713#if HAVE_ST_BLKSIZE
714 send_data(fin, dout, st.st_blksize);
715#else /* HAVE_ST_BLKSIZE */
716 send_data(fin, dout, BUFSIZ);
717#endif /* HAVE_ST_BLKSIZE */
718 fclose(dout);
719 data = -1;
720 pdata = -1;
721done:
722 (*closefunc) (fin);
723}
724
725VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
726{
727 FILE *fout, *din;
728 struct stat st;
729 int (*closefunc) ();
730
731 if (unique && stat(name, &st) == 0 &&
732 (name = gunique(name)) == NULL)
733 return;
734
735 if (restart_point)
736 mode = "r+w";
737 fout = fopen(name, mode);
738 closefunc = fclose;
739 if (fout == NULL) {
740 perror_reply(553, name);
741 return;
742 }
743 if (restart_point) {
744 if (type == TYPE_A) {
745 register int i, n, c;
746
747 n = restart_point;
748 i = 0;
749 while (i++ < n) {
750 if ((c = getc(fout)) == EOF) {
751 perror_reply(550, name);
752 goto done;
753 }
754 if (c == '\n')
755 i++;
756 }
757 /* We must do this seek to "current" position because we are changing
758 from reading to writing. */
759 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
760 perror_reply(550, name);
761 goto done;
762 }
763 } else
764 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
765 perror_reply(550, name);
766 goto done;
767 }
768 }
769 din = dataconn(name, (off_t) - 1, "r");
770 if (din == NULL)
771 goto done;
772 if (receive_data(din, fout) == 0) {
773 if (unique)
774 reply(226, "Transfer complete (unique file name:%s).",
775 name);
776 else
777 reply(226, "Transfer complete.");
778 }
779 fclose(din);
780 data = -1;
781 pdata = -1;
782done:
783 (*closefunc) (fout);
784}
785
786static FILE *getdatasock FUNCTION((mode), char *mode)
787{
788 int s, on = 1, tries;
789
790 if (data >= 0)
791 return (fdopen(data, mode));
792 disable_signalling();
793 if (seteuid((uid_t) 0))
794 syslog(LOG_ERR, "Can't set euid");
795 s = socket(AF_INET, SOCK_STREAM, 0);
796 if (s < 0)
797 goto bad;
798 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
799 (char *) &on, sizeof(on)) < 0)
800 goto bad;
801 /* anchor socket to avoid multi-homing problems */
802 data_source.sin_family = AF_INET;
803 data_source.sin_addr = ctrl_addr.sin_addr;
804 for (tries = 1;; tries++) {
805 if (bind(s, (struct sockaddr *) & data_source,
806 sizeof(data_source)) >= 0)
807 break;
808 if (errno != EADDRINUSE || tries > 10)
809 goto bad;
810 sleep(tries);
811 }
812 if (seteuid((uid_t) pw->pw_uid))
813 syslog(LOG_ERR, "Can't set euid");
814 enable_signalling();
815#ifdef IP_TOS
816 on = IPTOS_THROUGHPUT;
817 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
818 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
819#endif
820 return (fdopen(s, mode));
821bad:
822 {
823 int t = errno;
824
825 if (seteuid((uid_t) pw->pw_uid))
826 syslog(LOG_ERR, "Can't set euid");
827 enable_signalling();
828 close(s);
829
830 errno = t;
831 }
832 return (NULL);
833}
834
835static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
836{
837 char sizebuf[32];
838 FILE *file;
839 int retry = 0;
840#ifdef IP_TOS
841 int tos;
842#endif /* IP_TOS */
843
844 file_size = size;
845 byte_count = 0;
846 if (size != (off_t) - 1)
847 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
848 else
849 strcpy(sizebuf, "");
850 if (pdata >= 0) {
851 struct sockaddr_in from;
852 int s, fromlen = sizeof(from);
853
854 s = accept(pdata, (struct sockaddr *) & from, &fromlen);
855 if (s < 0) {
856 reply(425, "Can't open data connection.");
857 close(pdata);
858 pdata = -1;
859 return (NULL);
860 }
861 close(pdata);
862 pdata = s;
863#ifdef IP_TOS
864 tos = IPTOS_LOWDELAY;
865 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
866 sizeof(int));
867
868#endif
869 reply(150, "Opening %s mode data connection for %s%s.",
870 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
871 return (fdopen(pdata, mode));
872 }
873 if (data >= 0) {
874 reply(125, "Using existing data connection for %s%s.",
875 name, sizebuf);
876 usedefault = 1;
877 return (fdopen(data, mode));
878 }
879 if (usedefault)
880 data_dest = his_addr;
881 usedefault = 1;
882 file = getdatasock(mode);
883 if (file == NULL) {
884 reply(425, "Can't create data socket (%s,%d): %s.",
885 inet_ntoa(data_source.sin_addr),
886 ntohs(data_source.sin_port), strerror(errno));
887 return (NULL);
888 }
889 data = fileno(file);
890 while (connect(data, (struct sockaddr *) & data_dest,
891 sizeof(data_dest)) < 0) {
892 if (errno == EADDRINUSE && retry < swaitmax) {
893 sleep((unsigned) swaitint);
894 retry += swaitint;
895 continue;
896 }
897 perror_reply(425, "Can't build data connection");
898 fclose(file);
899 data = -1;
900 return (NULL);
901 }
902 reply(150, "Opening %s mode data connection for %s%s.",
903 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
904 return (file);
905}
906
907/*
908 * Tranfer the contents of "instr" to
909 * "outstr" peer using the appropriate
910 * encapsulation of the data subject
911 * to Mode, Structure, and Type.
912 *
913 * NB: Form isn't handled.
914 */
915static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
916{
917 register int c, cnt;
918 register char *buf;
919 int netfd, filefd;
920
921 transflag++;
922 if (setjmp(urgcatch)) {
923 transflag = 0;
924 return;
925 }
926 switch (type) {
927
928 case TYPE_A:
929 while ((c = getc(instr)) != EOF) {
930 byte_count++;
931 if (c == '\n') {
932 if (ferror(outstr))
933 goto data_err;
934 putc('\r', outstr);
935 }
936 putc(c, outstr);
937 }
938 fflush(outstr);
939 transflag = 0;
940 if (ferror(instr))
941 goto file_err;
942 if (ferror(outstr))
943 goto data_err;
944 reply(226, "Transfer complete.");
945 return;
946
947 case TYPE_I:
948 case TYPE_L:
949 if ((buf = malloc((u_int) blksize)) == NULL) {
950 transflag = 0;
951 perror_reply(451, "Local resource failure: malloc");
952 return;
953 }
954 netfd = fileno(outstr);
955 filefd = fileno(instr);
956 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
957 write(netfd, buf, cnt) == cnt)
958 byte_count += cnt;
959 transflag = 0;
960 free(buf);
961 if (cnt != 0) {
962 if (cnt < 0)
963 goto file_err;
964 goto data_err;
965 }
966 reply(226, "Transfer complete.");
967 return;
968 default:
969 transflag = 0;
970 reply(550, "Unimplemented TYPE %d in send_data", type);
971 return;
972 }
973
974data_err:
975 transflag = 0;
976 perror_reply(426, "Data connection");
977 return;
978
979file_err:
980 transflag = 0;
981 perror_reply(551, "Error on input file");
982}
983
984/*
985 * Transfer data from peer to
986 * "outstr" using the appropriate
987 * encapulation of the data subject
988 * to Mode, Structure, and Type.
989 *
990 * N.B.: Form isn't handled.
991 */
992static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
993{
994 register int c;
995 int cnt, bare_lfs = 0;
996 char buf[BUFSIZ];
997
998 transflag++;
999 if (setjmp(urgcatch)) {
1000 transflag = 0;
1001 return (-1);
1002 }
1003 switch (type) {
1004
1005 case TYPE_I:
1006 case TYPE_L:
1007 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1008 if (write(fileno(outstr), buf, cnt) != cnt)
1009 goto file_err;
1010 byte_count += cnt;
1011 }
1012 if (cnt < 0)
1013 goto data_err;
1014 transflag = 0;
1015 return (0);
1016
1017 case TYPE_E:
1018 reply(553, "TYPE E not implemented.");
1019 transflag = 0;
1020 return (-1);
1021
1022 case TYPE_A:
1023 while ((c = getc(instr)) != EOF) {
1024 byte_count++;
1025 if (c == '\n')
1026 bare_lfs++;
1027 while (c == '\r') {
1028 if (ferror(outstr))
1029 goto data_err;
1030 if ((c = getc(instr)) != '\n') {
1031 putc('\r', outstr);
1032 if (c == '\0' || c == EOF)
1033 goto contin2;
1034 }
1035 }
1036 putc(c, outstr);
1037 contin2:;
1038 }
1039 fflush(outstr);
1040 if (ferror(instr))
1041 goto data_err;
1042 if (ferror(outstr))
1043 goto file_err;
1044 transflag = 0;
1045 if (bare_lfs) {
1046 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1047 printf(" File may not have transferred correctly.\r\n");
1048 }
1049 return (0);
1050 default:
1051 reply(550, "Unimplemented TYPE %d in receive_data", type);
1052 transflag = 0;
1053 return (-1);
1054 }
1055
1056data_err:
1057 transflag = 0;
1058 perror_reply(426, "Data Connection");
1059 return (-1);
1060
1061file_err:
1062 transflag = 0;
1063 perror_reply(452, "Error writing file");
1064 return (-1);
1065}
1066
1067VOIDRET statfilecmd FUNCTION((filename), char *filename)
1068{
1069 char line[BUFSIZ];
1070 FILE *fin;
1071 int c;
1072
1073#if HAVE_LS_G_FLAG
1074 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1075#else /* HAVE_LS_G_FLAG */
1076 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1077#endif /* HAVE_LS_G_FLAG */
1078 fin = ftpd_popen(line, "r");
1079 lreply(211, "status of %s:", filename);
1080 while ((c = getc(fin)) != EOF) {
1081 if (c == '\n') {
1082 if (ferror(stdout)) {
1083 perror_reply(421, "control connection");
1084 ftpd_pclose(fin);
1085 dologout(1);
1086 /* NOTREACHED */
1087 }
1088 if (ferror(fin)) {
1089 perror_reply(551, filename);
1090 ftpd_pclose(fin);
1091 return;
1092 }
1093 putc('\r', stdout);
1094 }
1095 putc(c, stdout);
1096 }
1097 ftpd_pclose(fin);
1098 reply(211, "End of Status");
1099}
1100
1101VOIDRET statcmd FUNCTION_NOARGS
1102{
1103/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1104 struct sockaddr_in *sin;
1105 u_char *a, *p;
1106
1107 lreply(211, "FTP server status:");
1108 printf(" \r\n");
1109 printf(" Connected to %s", remotehost);
1110 if (!isdigit(remotehost[0]))
1111 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1112 printf("\r\n");
1113 if (logged_in) {
1114#if DOANONYMOUS
1115 if (guest)
1116 printf(" Logged in anonymously\r\n");
1117 else
1118#endif /* DOANONYMOUS */
1119 printf(" Logged in as %s\r\n", pw->pw_name);
1120 } else
1121 if (askpasswd)
1122 printf(" Waiting for password\r\n");
1123 else
1124 printf(" Waiting for user name\r\n");
1125 if (data != -1)
1126 printf(" Data connection open\r\n");
1127 else
1128 if (pdata != -1) {
1129 printf(" in Passive mode");
1130 sin = &pasv_addr;
1131 goto printaddr;
1132 } else
1133 if (usedefault == 0) {
1134 printf(" PORT");
1135 sin = &data_dest;
1136 printaddr:
1137 a = (u_char *) & sin->sin_addr;
1138 p = (u_char *) & sin->sin_port;
1139#define UC(b) (((int) b) & 0xff)
1140 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1141 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1142#undef UC
1143 } else
1144 printf(" No data connection\r\n");
1145 reply(211, "End of status");
1146}
1147
1148VOIDRET opiefatal FUNCTION((s), char *s)
1149{
1150 reply(451, "Error in server: %s\n", s);
1151 reply(221, "Closing connection due to server error.");
1152 dologout(0);
1153 /* NOTREACHED */
1154}
1155
1156static VOIDRET ack FUNCTION((s), char *s)
1157{
1158 reply(250, "%s command successful.", s);
1159}
1160
1161VOIDRET nack FUNCTION((s), char *s)
1162{
1163 reply(502, "%s command not implemented.", s);
1164}
1165
1166VOIDRET yyerror FUNCTION((s), char *s)
1167{
1168 char *cp;
1169
1170 if (cp = strchr(cbuf, '\n'))
1171 *cp = '\0';
1172 reply(500, "'%s': command not understood.", cbuf);
1173}
1174
1175VOIDRET delete FUNCTION((name), char *name)
1176{
1177 struct stat st;
1178
1179 if (stat(name, &st) < 0) {
1180 perror_reply(550, name);
1181 return;
1182 }
1183 if ((st.st_mode & S_IFMT) == S_IFDIR) {
1184 if (rmdir(name) < 0) {
1185 perror_reply(550, name);
1186 return;
1187 }
1188 goto done;
1189 }
1190 if (unlink(name) < 0) {
1191 perror_reply(550, name);
1192 return;
1193 }
1194done:
1195 ack("DELE");
1196}
1197
1198VOIDRET cwd FUNCTION((path), char *path)
1199{
1200 if (chdir(path) < 0)
1201 perror_reply(550, path);
1202 else
1203 ack("CWD");
1204}
1205
1206VOIDRET makedir FUNCTION((name), char *name)
1207{
1208 if (mkdir(name, 0777) < 0)
1209 perror_reply(550, name);
1210 else
1211 reply(257, "MKD command successful.");
1212}
1213
1214VOIDRET removedir FUNCTION((name), char *name)
1215{
1216 if (rmdir(name) < 0)
1217 perror_reply(550, name);
1218 else
1219 ack("RMD");
1220}
1221
1222VOIDRET pwd FUNCTION_NOARGS
1223{
1224 char path[MAXPATHLEN + 1];
1225
1226 if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1227 reply(550, "%s.", path);
1228 else
1229 reply(257, "\"%s\" is current directory.", path);
1230}
1231
1232char *renamefrom FUNCTION((name), char *name)
1233{
1234 struct stat st;
1235
1236 if (stat(name, &st) < 0) {
1237 perror_reply(550, name);
1238 return ((char *) 0);
1239 }
1240 reply(350, "File exists, ready for destination name");
1241 return (name);
1242}
1243
1244VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1245{
1246 if (rename(from, to) < 0)
1247 perror_reply(550, "rename");
1248 else
1249 ack("RNTO");
1250}
1251
1252static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1253{
1254 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1255 sizeof(struct in_addr), AF_INET);
1256 time_t t, time();
1257
1258 if (hp)
650 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
651 }
652 home = pw->pw_dir; /* home dir for globbing */
653 umask(defumask);
654 return;
655
656bad:
657 /* Forget all about it... */
658 end_login();
659}
660
661VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
662{
663 FILE *fin, *dout;
664 struct stat st;
665 int (*closefunc) ();
666
667 if (cmd == 0) {
668 fin = fopen(name, "r"), closefunc = fclose;
669 st.st_size = 0;
670 } else {
671 char line[BUFSIZ];
672
673 snprintf(line, sizeof(line), cmd, name);
674 name = line;
675 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
676 st.st_size = -1;
677#if HAVE_ST_BLKSIZE
678 st.st_blksize = BUFSIZ;
679#endif /* HAVE_ST_BLKSIZE */
680 }
681 if (fin == NULL) {
682 if (errno != 0)
683 perror_reply(550, name);
684 return;
685 }
686 if (cmd == 0 &&
687 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
688 reply(550, "%s: not a plain file.", name);
689 goto done;
690 }
691 if (restart_point) {
692 if (type == TYPE_A) {
693 register int i, n, c;
694
695 n = restart_point;
696 i = 0;
697 while (i++ < n) {
698 if ((c = getc(fin)) == EOF) {
699 perror_reply(550, name);
700 goto done;
701 }
702 if (c == '\n')
703 i++;
704 }
705 } else
706 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
707 perror_reply(550, name);
708 goto done;
709 }
710 }
711 dout = dataconn(name, st.st_size, "w");
712 if (dout == NULL)
713 goto done;
714#if HAVE_ST_BLKSIZE
715 send_data(fin, dout, st.st_blksize);
716#else /* HAVE_ST_BLKSIZE */
717 send_data(fin, dout, BUFSIZ);
718#endif /* HAVE_ST_BLKSIZE */
719 fclose(dout);
720 data = -1;
721 pdata = -1;
722done:
723 (*closefunc) (fin);
724}
725
726VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
727{
728 FILE *fout, *din;
729 struct stat st;
730 int (*closefunc) ();
731
732 if (unique && stat(name, &st) == 0 &&
733 (name = gunique(name)) == NULL)
734 return;
735
736 if (restart_point)
737 mode = "r+w";
738 fout = fopen(name, mode);
739 closefunc = fclose;
740 if (fout == NULL) {
741 perror_reply(553, name);
742 return;
743 }
744 if (restart_point) {
745 if (type == TYPE_A) {
746 register int i, n, c;
747
748 n = restart_point;
749 i = 0;
750 while (i++ < n) {
751 if ((c = getc(fout)) == EOF) {
752 perror_reply(550, name);
753 goto done;
754 }
755 if (c == '\n')
756 i++;
757 }
758 /* We must do this seek to "current" position because we are changing
759 from reading to writing. */
760 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
761 perror_reply(550, name);
762 goto done;
763 }
764 } else
765 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
766 perror_reply(550, name);
767 goto done;
768 }
769 }
770 din = dataconn(name, (off_t) - 1, "r");
771 if (din == NULL)
772 goto done;
773 if (receive_data(din, fout) == 0) {
774 if (unique)
775 reply(226, "Transfer complete (unique file name:%s).",
776 name);
777 else
778 reply(226, "Transfer complete.");
779 }
780 fclose(din);
781 data = -1;
782 pdata = -1;
783done:
784 (*closefunc) (fout);
785}
786
787static FILE *getdatasock FUNCTION((mode), char *mode)
788{
789 int s, on = 1, tries;
790
791 if (data >= 0)
792 return (fdopen(data, mode));
793 disable_signalling();
794 if (seteuid((uid_t) 0))
795 syslog(LOG_ERR, "Can't set euid");
796 s = socket(AF_INET, SOCK_STREAM, 0);
797 if (s < 0)
798 goto bad;
799 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
800 (char *) &on, sizeof(on)) < 0)
801 goto bad;
802 /* anchor socket to avoid multi-homing problems */
803 data_source.sin_family = AF_INET;
804 data_source.sin_addr = ctrl_addr.sin_addr;
805 for (tries = 1;; tries++) {
806 if (bind(s, (struct sockaddr *) & data_source,
807 sizeof(data_source)) >= 0)
808 break;
809 if (errno != EADDRINUSE || tries > 10)
810 goto bad;
811 sleep(tries);
812 }
813 if (seteuid((uid_t) pw->pw_uid))
814 syslog(LOG_ERR, "Can't set euid");
815 enable_signalling();
816#ifdef IP_TOS
817 on = IPTOS_THROUGHPUT;
818 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
819 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
820#endif
821 return (fdopen(s, mode));
822bad:
823 {
824 int t = errno;
825
826 if (seteuid((uid_t) pw->pw_uid))
827 syslog(LOG_ERR, "Can't set euid");
828 enable_signalling();
829 close(s);
830
831 errno = t;
832 }
833 return (NULL);
834}
835
836static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
837{
838 char sizebuf[32];
839 FILE *file;
840 int retry = 0;
841#ifdef IP_TOS
842 int tos;
843#endif /* IP_TOS */
844
845 file_size = size;
846 byte_count = 0;
847 if (size != (off_t) - 1)
848 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
849 else
850 strcpy(sizebuf, "");
851 if (pdata >= 0) {
852 struct sockaddr_in from;
853 int s, fromlen = sizeof(from);
854
855 s = accept(pdata, (struct sockaddr *) & from, &fromlen);
856 if (s < 0) {
857 reply(425, "Can't open data connection.");
858 close(pdata);
859 pdata = -1;
860 return (NULL);
861 }
862 close(pdata);
863 pdata = s;
864#ifdef IP_TOS
865 tos = IPTOS_LOWDELAY;
866 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
867 sizeof(int));
868
869#endif
870 reply(150, "Opening %s mode data connection for %s%s.",
871 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
872 return (fdopen(pdata, mode));
873 }
874 if (data >= 0) {
875 reply(125, "Using existing data connection for %s%s.",
876 name, sizebuf);
877 usedefault = 1;
878 return (fdopen(data, mode));
879 }
880 if (usedefault)
881 data_dest = his_addr;
882 usedefault = 1;
883 file = getdatasock(mode);
884 if (file == NULL) {
885 reply(425, "Can't create data socket (%s,%d): %s.",
886 inet_ntoa(data_source.sin_addr),
887 ntohs(data_source.sin_port), strerror(errno));
888 return (NULL);
889 }
890 data = fileno(file);
891 while (connect(data, (struct sockaddr *) & data_dest,
892 sizeof(data_dest)) < 0) {
893 if (errno == EADDRINUSE && retry < swaitmax) {
894 sleep((unsigned) swaitint);
895 retry += swaitint;
896 continue;
897 }
898 perror_reply(425, "Can't build data connection");
899 fclose(file);
900 data = -1;
901 return (NULL);
902 }
903 reply(150, "Opening %s mode data connection for %s%s.",
904 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
905 return (file);
906}
907
908/*
909 * Tranfer the contents of "instr" to
910 * "outstr" peer using the appropriate
911 * encapsulation of the data subject
912 * to Mode, Structure, and Type.
913 *
914 * NB: Form isn't handled.
915 */
916static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
917{
918 register int c, cnt;
919 register char *buf;
920 int netfd, filefd;
921
922 transflag++;
923 if (setjmp(urgcatch)) {
924 transflag = 0;
925 return;
926 }
927 switch (type) {
928
929 case TYPE_A:
930 while ((c = getc(instr)) != EOF) {
931 byte_count++;
932 if (c == '\n') {
933 if (ferror(outstr))
934 goto data_err;
935 putc('\r', outstr);
936 }
937 putc(c, outstr);
938 }
939 fflush(outstr);
940 transflag = 0;
941 if (ferror(instr))
942 goto file_err;
943 if (ferror(outstr))
944 goto data_err;
945 reply(226, "Transfer complete.");
946 return;
947
948 case TYPE_I:
949 case TYPE_L:
950 if ((buf = malloc((u_int) blksize)) == NULL) {
951 transflag = 0;
952 perror_reply(451, "Local resource failure: malloc");
953 return;
954 }
955 netfd = fileno(outstr);
956 filefd = fileno(instr);
957 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
958 write(netfd, buf, cnt) == cnt)
959 byte_count += cnt;
960 transflag = 0;
961 free(buf);
962 if (cnt != 0) {
963 if (cnt < 0)
964 goto file_err;
965 goto data_err;
966 }
967 reply(226, "Transfer complete.");
968 return;
969 default:
970 transflag = 0;
971 reply(550, "Unimplemented TYPE %d in send_data", type);
972 return;
973 }
974
975data_err:
976 transflag = 0;
977 perror_reply(426, "Data connection");
978 return;
979
980file_err:
981 transflag = 0;
982 perror_reply(551, "Error on input file");
983}
984
985/*
986 * Transfer data from peer to
987 * "outstr" using the appropriate
988 * encapulation of the data subject
989 * to Mode, Structure, and Type.
990 *
991 * N.B.: Form isn't handled.
992 */
993static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
994{
995 register int c;
996 int cnt, bare_lfs = 0;
997 char buf[BUFSIZ];
998
999 transflag++;
1000 if (setjmp(urgcatch)) {
1001 transflag = 0;
1002 return (-1);
1003 }
1004 switch (type) {
1005
1006 case TYPE_I:
1007 case TYPE_L:
1008 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1009 if (write(fileno(outstr), buf, cnt) != cnt)
1010 goto file_err;
1011 byte_count += cnt;
1012 }
1013 if (cnt < 0)
1014 goto data_err;
1015 transflag = 0;
1016 return (0);
1017
1018 case TYPE_E:
1019 reply(553, "TYPE E not implemented.");
1020 transflag = 0;
1021 return (-1);
1022
1023 case TYPE_A:
1024 while ((c = getc(instr)) != EOF) {
1025 byte_count++;
1026 if (c == '\n')
1027 bare_lfs++;
1028 while (c == '\r') {
1029 if (ferror(outstr))
1030 goto data_err;
1031 if ((c = getc(instr)) != '\n') {
1032 putc('\r', outstr);
1033 if (c == '\0' || c == EOF)
1034 goto contin2;
1035 }
1036 }
1037 putc(c, outstr);
1038 contin2:;
1039 }
1040 fflush(outstr);
1041 if (ferror(instr))
1042 goto data_err;
1043 if (ferror(outstr))
1044 goto file_err;
1045 transflag = 0;
1046 if (bare_lfs) {
1047 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1048 printf(" File may not have transferred correctly.\r\n");
1049 }
1050 return (0);
1051 default:
1052 reply(550, "Unimplemented TYPE %d in receive_data", type);
1053 transflag = 0;
1054 return (-1);
1055 }
1056
1057data_err:
1058 transflag = 0;
1059 perror_reply(426, "Data Connection");
1060 return (-1);
1061
1062file_err:
1063 transflag = 0;
1064 perror_reply(452, "Error writing file");
1065 return (-1);
1066}
1067
1068VOIDRET statfilecmd FUNCTION((filename), char *filename)
1069{
1070 char line[BUFSIZ];
1071 FILE *fin;
1072 int c;
1073
1074#if HAVE_LS_G_FLAG
1075 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1076#else /* HAVE_LS_G_FLAG */
1077 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1078#endif /* HAVE_LS_G_FLAG */
1079 fin = ftpd_popen(line, "r");
1080 lreply(211, "status of %s:", filename);
1081 while ((c = getc(fin)) != EOF) {
1082 if (c == '\n') {
1083 if (ferror(stdout)) {
1084 perror_reply(421, "control connection");
1085 ftpd_pclose(fin);
1086 dologout(1);
1087 /* NOTREACHED */
1088 }
1089 if (ferror(fin)) {
1090 perror_reply(551, filename);
1091 ftpd_pclose(fin);
1092 return;
1093 }
1094 putc('\r', stdout);
1095 }
1096 putc(c, stdout);
1097 }
1098 ftpd_pclose(fin);
1099 reply(211, "End of Status");
1100}
1101
1102VOIDRET statcmd FUNCTION_NOARGS
1103{
1104/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1105 struct sockaddr_in *sin;
1106 u_char *a, *p;
1107
1108 lreply(211, "FTP server status:");
1109 printf(" \r\n");
1110 printf(" Connected to %s", remotehost);
1111 if (!isdigit(remotehost[0]))
1112 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1113 printf("\r\n");
1114 if (logged_in) {
1115#if DOANONYMOUS
1116 if (guest)
1117 printf(" Logged in anonymously\r\n");
1118 else
1119#endif /* DOANONYMOUS */
1120 printf(" Logged in as %s\r\n", pw->pw_name);
1121 } else
1122 if (askpasswd)
1123 printf(" Waiting for password\r\n");
1124 else
1125 printf(" Waiting for user name\r\n");
1126 if (data != -1)
1127 printf(" Data connection open\r\n");
1128 else
1129 if (pdata != -1) {
1130 printf(" in Passive mode");
1131 sin = &pasv_addr;
1132 goto printaddr;
1133 } else
1134 if (usedefault == 0) {
1135 printf(" PORT");
1136 sin = &data_dest;
1137 printaddr:
1138 a = (u_char *) & sin->sin_addr;
1139 p = (u_char *) & sin->sin_port;
1140#define UC(b) (((int) b) & 0xff)
1141 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1142 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1143#undef UC
1144 } else
1145 printf(" No data connection\r\n");
1146 reply(211, "End of status");
1147}
1148
1149VOIDRET opiefatal FUNCTION((s), char *s)
1150{
1151 reply(451, "Error in server: %s\n", s);
1152 reply(221, "Closing connection due to server error.");
1153 dologout(0);
1154 /* NOTREACHED */
1155}
1156
1157static VOIDRET ack FUNCTION((s), char *s)
1158{
1159 reply(250, "%s command successful.", s);
1160}
1161
1162VOIDRET nack FUNCTION((s), char *s)
1163{
1164 reply(502, "%s command not implemented.", s);
1165}
1166
1167VOIDRET yyerror FUNCTION((s), char *s)
1168{
1169 char *cp;
1170
1171 if (cp = strchr(cbuf, '\n'))
1172 *cp = '\0';
1173 reply(500, "'%s': command not understood.", cbuf);
1174}
1175
1176VOIDRET delete FUNCTION((name), char *name)
1177{
1178 struct stat st;
1179
1180 if (stat(name, &st) < 0) {
1181 perror_reply(550, name);
1182 return;
1183 }
1184 if ((st.st_mode & S_IFMT) == S_IFDIR) {
1185 if (rmdir(name) < 0) {
1186 perror_reply(550, name);
1187 return;
1188 }
1189 goto done;
1190 }
1191 if (unlink(name) < 0) {
1192 perror_reply(550, name);
1193 return;
1194 }
1195done:
1196 ack("DELE");
1197}
1198
1199VOIDRET cwd FUNCTION((path), char *path)
1200{
1201 if (chdir(path) < 0)
1202 perror_reply(550, path);
1203 else
1204 ack("CWD");
1205}
1206
1207VOIDRET makedir FUNCTION((name), char *name)
1208{
1209 if (mkdir(name, 0777) < 0)
1210 perror_reply(550, name);
1211 else
1212 reply(257, "MKD command successful.");
1213}
1214
1215VOIDRET removedir FUNCTION((name), char *name)
1216{
1217 if (rmdir(name) < 0)
1218 perror_reply(550, name);
1219 else
1220 ack("RMD");
1221}
1222
1223VOIDRET pwd FUNCTION_NOARGS
1224{
1225 char path[MAXPATHLEN + 1];
1226
1227 if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1228 reply(550, "%s.", path);
1229 else
1230 reply(257, "\"%s\" is current directory.", path);
1231}
1232
1233char *renamefrom FUNCTION((name), char *name)
1234{
1235 struct stat st;
1236
1237 if (stat(name, &st) < 0) {
1238 perror_reply(550, name);
1239 return ((char *) 0);
1240 }
1241 reply(350, "File exists, ready for destination name");
1242 return (name);
1243}
1244
1245VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1246{
1247 if (rename(from, to) < 0)
1248 perror_reply(550, "rename");
1249 else
1250 ack("RNTO");
1251}
1252
1253static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1254{
1255 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1256 sizeof(struct in_addr), AF_INET);
1257 time_t t, time();
1258
1259 if (hp)
1259 strncpy(remotehost, hp->h_name, sizeof(remotehost));
1260 opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1260 else
1261 else
1261 strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1262 remotehost[sizeof(remotehost) - 1] = '\0';
1262 opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1263#if DOTITLE
1263#if DOTITLE
1264 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1265 setproctitle("%s", proctitle);
1264 setproctitle("%s: connected", remotehost);
1266#endif /* DOTITLE */
1267
1268 t = time((time_t *) 0);
1269 syslog(LOG_INFO, "connection from %s at %s",
1270 remotehost, ctime(&t));
1271}
1272
1273/*
1274 * Record logout in wtmp file
1275 * and exit with supplied status.
1276 */
1277VOIDRET dologout FUNCTION((status), int status)
1278{
1279 disable_signalling();
1280 if (logged_in) {
1281 if (seteuid((uid_t) 0))
1282 syslog(LOG_ERR, "Can't set euid");
1265#endif /* DOTITLE */
1266
1267 t = time((time_t *) 0);
1268 syslog(LOG_INFO, "connection from %s at %s",
1269 remotehost, ctime(&t));
1270}
1271
1272/*
1273 * Record logout in wtmp file
1274 * and exit with supplied status.
1275 */
1276VOIDRET dologout FUNCTION((status), int status)
1277{
1278 disable_signalling();
1279 if (logged_in) {
1280 if (seteuid((uid_t) 0))
1281 syslog(LOG_ERR, "Can't set euid");
1283 opielogwtmp(ttyline, "", "");
1282 opielogwtmp(ttyline, "", "", "ftp");
1284 }
1285 /* beware of flushing buffers after a SIGPIPE */
1286 _exit(status);
1287}
1288
1289static VOIDRET myoob FUNCTION((input), int input)
1290{
1291 char *cp;
1292
1293 /* only process if transfer occurring */
1294 if (!transflag)
1295 return;
1296 cp = tmpline;
1297 if (getline(cp, 7, stdin) == NULL) {
1298 reply(221, "You could at least say goodbye.");
1299 dologout(0);
1300 }
1301 upper(cp);
1302 if (strcmp(cp, "ABOR\r\n") == 0) {
1303 tmpline[0] = '\0';
1304 reply(426, "Transfer aborted. Data connection closed.");
1305 reply(226, "Abort successful");
1306 longjmp(urgcatch, 1);
1307 }
1308 if (strcmp(cp, "STAT\r\n") == 0) {
1309 if (file_size != (off_t) - 1)
1310 reply(213, "Status: %lu of %lu bytes transferred",
1311 byte_count, file_size);
1312 else
1313 reply(213, "Status: %lu bytes transferred", byte_count);
1314 }
1315}
1316
1317/*
1318 * Note: a response of 425 is not mentioned as a possible response to
1319 * the PASV command in RFC959. However, it has been blessed as
1320 * a legitimate response by Jon Postel in a telephone conversation
1321 * with Rick Adams on 25 Jan 89.
1322 */
1323VOIDRET passive FUNCTION_NOARGS
1324{
1325 int len;
1326 register char *p, *a;
1327
1328 pdata = socket(AF_INET, SOCK_STREAM, 0);
1329 if (pdata < 0) {
1330 perror_reply(425, "Can't open passive connection");
1331 return;
1332 }
1333 pasv_addr = ctrl_addr;
1334 pasv_addr.sin_port = 0;
1335 if (seteuid((uid_t) 0))
1336 syslog(LOG_ERR, "Can't set euid");
1337 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1338 seteuid((uid_t) pw->pw_uid);
1339 goto pasv_error;
1340 }
1341 if (seteuid((uid_t) pw->pw_uid))
1342 syslog(LOG_ERR, "Can't set euid");
1343 len = sizeof(pasv_addr);
1344 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1345 goto pasv_error;
1346 if (listen(pdata, 1) < 0)
1347 goto pasv_error;
1348 a = (char *) &pasv_addr.sin_addr;
1349 p = (char *) &pasv_addr.sin_port;
1350
1351#define UC(b) (((int) b) & 0xff)
1352
1353 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1354 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1355 return;
1356
1357pasv_error:
1358 close(pdata);
1359 pdata = -1;
1360 perror_reply(425, "Can't open passive connection");
1361 return;
1362}
1363
1364/*
1365 * Generate unique name for file with basename "local".
1366 * The file named "local" is already known to exist.
1367 * Generates failure reply on error.
1368 */
1369static char *gunique FUNCTION((local), char *local)
1370{
1371 static char new[MAXPATHLEN+1];
1372 struct stat st;
1373 char *cp = strrchr(local, '/');
1374 int count = 0;
1375
1376 if (cp)
1377 *cp = '\0';
1378 if (stat(cp ? local : ".", &st) < 0) {
1379 perror_reply(553, cp ? local : ".");
1380 return ((char *) 0);
1381 }
1382 if (cp)
1383 *cp = '/';
1384 strcpy(new, local);
1385 cp = new + strlen(new);
1386 *cp++ = '.';
1387 for (count = 1; count < 100; count++) {
1388 snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1389 if (stat(new, &st) < 0)
1390 return (new);
1391 }
1392 reply(452, "Unique file name cannot be created.");
1393 return ((char *) 0);
1394}
1395
1396/*
1397 * Format and send reply containing system error number.
1398 */
1399VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1400{
1401 reply(code, "%s: %s.", string, strerror(errno));
1402}
1403
1404static char *onefile[] =
1405{
1406 "",
1407 0
1408};
1409
1410VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1411{
1412 struct stat st;
1413 DIR *dirp = NULL;
1414 struct dirent *dir;
1415 FILE *dout = NULL;
1416 register char **dirlist, *dirname;
1417 int simple = 0;
1418
1419 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1420 extern char **ftpglob(), *globerr;
1421
1422 globerr = NULL;
1423 dirlist = ftpglob(whichfiles);
1424 if (globerr != NULL) {
1425 reply(550, globerr);
1426 return;
1427 } else
1428 if (dirlist == NULL) {
1429 errno = ENOENT;
1430 perror_reply(550, whichfiles);
1431 return;
1432 }
1433 } else {
1434 onefile[0] = whichfiles;
1435 dirlist = onefile;
1436 simple = 1;
1437 }
1438
1439 if (setjmp(urgcatch)) {
1440 transflag = 0;
1441 return;
1442 }
1443 while (dirname = *dirlist++) {
1444 if (stat(dirname, &st) < 0) {
1445 /* If user typed "ls -l", etc, and the client used NLST, do what the
1446 user meant. */
1447 if (dirname[0] == '-' && *dirlist == NULL &&
1448 transflag == 0) {
1449 retrieve("/bin/ls %s", dirname);
1450 return;
1451 }
1452 perror_reply(550, whichfiles);
1453 if (dout != NULL) {
1454 fclose(dout);
1455 transflag = 0;
1456 data = -1;
1457 pdata = -1;
1458 }
1459 return;
1460 }
1461 if ((st.st_mode & S_IFMT) == S_IFREG) {
1462 if (dout == NULL) {
1463 dout = dataconn("file list", (off_t) - 1, "w");
1464 if (dout == NULL)
1465 return;
1466 transflag++;
1467 }
1468 fprintf(dout, "%s%s\n", dirname,
1469 type == TYPE_A ? "\r" : "");
1470 byte_count += strlen(dirname) + 1;
1471 continue;
1472 } else
1473 if ((st.st_mode & S_IFMT) != S_IFDIR)
1474 continue;
1475
1476 if ((dirp = opendir(dirname)) == NULL)
1477 continue;
1478
1479 while ((dir = readdir(dirp)) != NULL) {
1480 char nbuf[MAXPATHLEN+1];
1481
1482 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1483 continue;
1484 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1485 (strlen(dir->d_name) == 2))
1486 continue;
1487
1488 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1489
1490 /* We have to do a stat to insure it's not a directory or special file. */
1491 if (simple || (stat(nbuf, &st) == 0 &&
1492 (st.st_mode & S_IFMT) == S_IFREG)) {
1493 if (dout == NULL) {
1494 dout = dataconn("file list", (off_t) - 1, "w");
1495 if (dout == NULL)
1496 return;
1497 transflag++;
1498 }
1499 if (nbuf[0] == '.' && nbuf[1] == '/')
1500 fprintf(dout, "%s%s\n", &nbuf[2],
1501 type == TYPE_A ? "\r" : "");
1502 else
1503 fprintf(dout, "%s%s\n", nbuf,
1504 type == TYPE_A ? "\r" : "");
1505 byte_count += strlen(nbuf) + 1;
1506 }
1507 }
1508 closedir(dirp);
1509 }
1510
1511 if (dout == NULL)
1512 reply(550, "No files found.");
1513 else
1514 if (ferror(dout) != 0)
1515 perror_reply(550, "Data connection");
1516 else
1517 reply(226, "Transfer complete.");
1518
1519 transflag = 0;
1520 if (dout != NULL)
1521 fclose(dout);
1522 data = -1;
1523 pdata = -1;
1524}
1525
1526#if DOTITLE
1527/*
1528 * clobber argv so ps will show what we're doing.
1529 * (stolen from sendmail)
1530 * warning, since this is usually started from inetd.conf, it
1531 * often doesn't have much of an environment or arglist to overwrite.
1532 */
1533VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1534{
1535 register char *p, *bp, ch;
1536 register int i;
1537 char buf[BUFSIZ];
1538
1539 snprintf(buf, sizeof(buf), fmt, a, b, c);
1540
1541 /* make ps print our process name */
1542 p = Argv[0];
1543 *p++ = '-';
1544
1545 i = strlen(buf);
1546 if (i > LastArgv - p - 2) {
1547 i = LastArgv - p - 2;
1548 buf[i] = '\0';
1549 }
1550 bp = buf;
1551 while (ch = *bp++)
1552 if (ch != '\n' && ch != '\r')
1553 *p++ = ch;
1554 while (p < LastArgv)
1555 *p++ = ' ';
1556}
1557#endif /* DOTITLE */
1558
1559VOIDRET catchexit FUNCTION_NOARGS
1560{
1561 closelog();
1562}
1563
1564int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1565{
1566 int addrlen, on = 1;
1567 char *cp;
1568#ifdef IP_TOS
1569 int tos;
1570#endif /* IP_TOS */
1571
1572 {
1573 int i;
1574
1575 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1576 close(i);
1577 }
1578
1579 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1580 anonymous ftp's that chroot and can't do it later. */
1581 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1582 atexit(catchexit);
1583 addrlen = sizeof(his_addr);
1584 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1585 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1586 exit(1);
1587 }
1588 addrlen = sizeof(ctrl_addr);
1589 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1590 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1591 exit(1);
1592 }
1593#ifdef IP_TOS
1594 tos = IPTOS_LOWDELAY;
1595 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1596 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1597#endif
1598 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1599 debug = 0;
1600#if DOTITLE
1601 /* Save start and extent of argv for setproctitle. */
1602 Argv = argv;
1603 while (*envp)
1604 envp++;
1605 LastArgv = envp[-1] + strlen(envp[-1]);
1606#endif /* DOTITLE */
1607
1608 argc--, argv++;
1609 while (argc > 0 && *argv[0] == '-') {
1610 for (cp = &argv[0][1]; *cp; cp++)
1611 switch (*cp) {
1612
1613 case 'v':
1614 debug = 1;
1615 break;
1616
1617 case 'd':
1618 debug = 1;
1619 break;
1620
1621 case 'l':
1622 break;
1623
1624 case 't':
1625 timeout = atoi(++cp);
1626 if (maxtimeout < timeout)
1627 maxtimeout = timeout;
1628 goto nextopt;
1629
1630 case 'T':
1631 maxtimeout = atoi(++cp);
1632 if (timeout > maxtimeout)
1633 timeout = maxtimeout;
1634 goto nextopt;
1635
1636 case 'u':
1637 {
1638 int val = 0;
1639
1640 while (*++cp && *cp >= '0' && *cp <= '9')
1641 val = val * 8 + *cp - '0';
1642 if (*cp)
1643 fprintf(stderr, "ftpd: Bad value for -u\n");
1644 else
1645 defumask = val;
1646 goto nextopt;
1647 }
1648
1649 default:
1650 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1651 *cp);
1652 break;
1653 }
1654nextopt:
1655 argc--, argv++;
1656 }
1657 freopen(_PATH_DEVNULL, "w", stderr);
1658 signal(SIGCHLD, SIG_IGN);
1659 enable_signalling();
1660
1661 /* Try to handle urgent data inline */
1662#ifdef SO_OOBINLINE
1663 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1664 syslog(LOG_ERR, "setsockopt: %m");
1665#endif
1666
1667#ifdef F_SETOWN
1668 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1669 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1670#endif
1671 dolog(&his_addr);
1672 /* Set up default state */
1673 data = -1;
1674 type = TYPE_A;
1675 form = FORM_N;
1676 stru = STRU_F;
1677 mode = MODE_S;
1678 tmpline[0] = '\0';
1679 af_pwok = opieaccessfile(remotehost);
1680
1681 {
1682 FILE *fd;
1683 char line[128];
1684
1685 /* If logins are disabled, print out the message. */
1686 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1687 while (fgets(line, sizeof(line), fd) != NULL) {
1688 if ((cp = strchr(line, '\n')) != NULL)
1689 *cp = '\0';
1690 lreply(530, "%s", line);
1691 }
1692 (void) fflush(stdout);
1693 (void) fclose(fd);
1694 reply(530, "System not available.");
1695 exit(0);
1696 }
1697 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1698 while (fgets(line, sizeof(line), fd) != NULL) {
1699 if ((cp = strchr(line, '\n')) != NULL)
1700 *cp = '\0';
1701 lreply(220, "%s", line);
1702 }
1703 (void) fflush(stdout);
1704 (void) fclose(fd);
1705 /* reply(220,) must follow */
1706 }
1707 };
1708
1709 reply(220, "FTP server ready.");
1710
1711 setjmp(errcatch);
1712 for (;;)
1713 yyparse();
1714 /* NOTREACHED */
1715 return 0;
1716}
1283 }
1284 /* beware of flushing buffers after a SIGPIPE */
1285 _exit(status);
1286}
1287
1288static VOIDRET myoob FUNCTION((input), int input)
1289{
1290 char *cp;
1291
1292 /* only process if transfer occurring */
1293 if (!transflag)
1294 return;
1295 cp = tmpline;
1296 if (getline(cp, 7, stdin) == NULL) {
1297 reply(221, "You could at least say goodbye.");
1298 dologout(0);
1299 }
1300 upper(cp);
1301 if (strcmp(cp, "ABOR\r\n") == 0) {
1302 tmpline[0] = '\0';
1303 reply(426, "Transfer aborted. Data connection closed.");
1304 reply(226, "Abort successful");
1305 longjmp(urgcatch, 1);
1306 }
1307 if (strcmp(cp, "STAT\r\n") == 0) {
1308 if (file_size != (off_t) - 1)
1309 reply(213, "Status: %lu of %lu bytes transferred",
1310 byte_count, file_size);
1311 else
1312 reply(213, "Status: %lu bytes transferred", byte_count);
1313 }
1314}
1315
1316/*
1317 * Note: a response of 425 is not mentioned as a possible response to
1318 * the PASV command in RFC959. However, it has been blessed as
1319 * a legitimate response by Jon Postel in a telephone conversation
1320 * with Rick Adams on 25 Jan 89.
1321 */
1322VOIDRET passive FUNCTION_NOARGS
1323{
1324 int len;
1325 register char *p, *a;
1326
1327 pdata = socket(AF_INET, SOCK_STREAM, 0);
1328 if (pdata < 0) {
1329 perror_reply(425, "Can't open passive connection");
1330 return;
1331 }
1332 pasv_addr = ctrl_addr;
1333 pasv_addr.sin_port = 0;
1334 if (seteuid((uid_t) 0))
1335 syslog(LOG_ERR, "Can't set euid");
1336 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1337 seteuid((uid_t) pw->pw_uid);
1338 goto pasv_error;
1339 }
1340 if (seteuid((uid_t) pw->pw_uid))
1341 syslog(LOG_ERR, "Can't set euid");
1342 len = sizeof(pasv_addr);
1343 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1344 goto pasv_error;
1345 if (listen(pdata, 1) < 0)
1346 goto pasv_error;
1347 a = (char *) &pasv_addr.sin_addr;
1348 p = (char *) &pasv_addr.sin_port;
1349
1350#define UC(b) (((int) b) & 0xff)
1351
1352 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1353 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1354 return;
1355
1356pasv_error:
1357 close(pdata);
1358 pdata = -1;
1359 perror_reply(425, "Can't open passive connection");
1360 return;
1361}
1362
1363/*
1364 * Generate unique name for file with basename "local".
1365 * The file named "local" is already known to exist.
1366 * Generates failure reply on error.
1367 */
1368static char *gunique FUNCTION((local), char *local)
1369{
1370 static char new[MAXPATHLEN+1];
1371 struct stat st;
1372 char *cp = strrchr(local, '/');
1373 int count = 0;
1374
1375 if (cp)
1376 *cp = '\0';
1377 if (stat(cp ? local : ".", &st) < 0) {
1378 perror_reply(553, cp ? local : ".");
1379 return ((char *) 0);
1380 }
1381 if (cp)
1382 *cp = '/';
1383 strcpy(new, local);
1384 cp = new + strlen(new);
1385 *cp++ = '.';
1386 for (count = 1; count < 100; count++) {
1387 snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1388 if (stat(new, &st) < 0)
1389 return (new);
1390 }
1391 reply(452, "Unique file name cannot be created.");
1392 return ((char *) 0);
1393}
1394
1395/*
1396 * Format and send reply containing system error number.
1397 */
1398VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1399{
1400 reply(code, "%s: %s.", string, strerror(errno));
1401}
1402
1403static char *onefile[] =
1404{
1405 "",
1406 0
1407};
1408
1409VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1410{
1411 struct stat st;
1412 DIR *dirp = NULL;
1413 struct dirent *dir;
1414 FILE *dout = NULL;
1415 register char **dirlist, *dirname;
1416 int simple = 0;
1417
1418 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1419 extern char **ftpglob(), *globerr;
1420
1421 globerr = NULL;
1422 dirlist = ftpglob(whichfiles);
1423 if (globerr != NULL) {
1424 reply(550, globerr);
1425 return;
1426 } else
1427 if (dirlist == NULL) {
1428 errno = ENOENT;
1429 perror_reply(550, whichfiles);
1430 return;
1431 }
1432 } else {
1433 onefile[0] = whichfiles;
1434 dirlist = onefile;
1435 simple = 1;
1436 }
1437
1438 if (setjmp(urgcatch)) {
1439 transflag = 0;
1440 return;
1441 }
1442 while (dirname = *dirlist++) {
1443 if (stat(dirname, &st) < 0) {
1444 /* If user typed "ls -l", etc, and the client used NLST, do what the
1445 user meant. */
1446 if (dirname[0] == '-' && *dirlist == NULL &&
1447 transflag == 0) {
1448 retrieve("/bin/ls %s", dirname);
1449 return;
1450 }
1451 perror_reply(550, whichfiles);
1452 if (dout != NULL) {
1453 fclose(dout);
1454 transflag = 0;
1455 data = -1;
1456 pdata = -1;
1457 }
1458 return;
1459 }
1460 if ((st.st_mode & S_IFMT) == S_IFREG) {
1461 if (dout == NULL) {
1462 dout = dataconn("file list", (off_t) - 1, "w");
1463 if (dout == NULL)
1464 return;
1465 transflag++;
1466 }
1467 fprintf(dout, "%s%s\n", dirname,
1468 type == TYPE_A ? "\r" : "");
1469 byte_count += strlen(dirname) + 1;
1470 continue;
1471 } else
1472 if ((st.st_mode & S_IFMT) != S_IFDIR)
1473 continue;
1474
1475 if ((dirp = opendir(dirname)) == NULL)
1476 continue;
1477
1478 while ((dir = readdir(dirp)) != NULL) {
1479 char nbuf[MAXPATHLEN+1];
1480
1481 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1482 continue;
1483 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1484 (strlen(dir->d_name) == 2))
1485 continue;
1486
1487 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1488
1489 /* We have to do a stat to insure it's not a directory or special file. */
1490 if (simple || (stat(nbuf, &st) == 0 &&
1491 (st.st_mode & S_IFMT) == S_IFREG)) {
1492 if (dout == NULL) {
1493 dout = dataconn("file list", (off_t) - 1, "w");
1494 if (dout == NULL)
1495 return;
1496 transflag++;
1497 }
1498 if (nbuf[0] == '.' && nbuf[1] == '/')
1499 fprintf(dout, "%s%s\n", &nbuf[2],
1500 type == TYPE_A ? "\r" : "");
1501 else
1502 fprintf(dout, "%s%s\n", nbuf,
1503 type == TYPE_A ? "\r" : "");
1504 byte_count += strlen(nbuf) + 1;
1505 }
1506 }
1507 closedir(dirp);
1508 }
1509
1510 if (dout == NULL)
1511 reply(550, "No files found.");
1512 else
1513 if (ferror(dout) != 0)
1514 perror_reply(550, "Data connection");
1515 else
1516 reply(226, "Transfer complete.");
1517
1518 transflag = 0;
1519 if (dout != NULL)
1520 fclose(dout);
1521 data = -1;
1522 pdata = -1;
1523}
1524
1525#if DOTITLE
1526/*
1527 * clobber argv so ps will show what we're doing.
1528 * (stolen from sendmail)
1529 * warning, since this is usually started from inetd.conf, it
1530 * often doesn't have much of an environment or arglist to overwrite.
1531 */
1532VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1533{
1534 register char *p, *bp, ch;
1535 register int i;
1536 char buf[BUFSIZ];
1537
1538 snprintf(buf, sizeof(buf), fmt, a, b, c);
1539
1540 /* make ps print our process name */
1541 p = Argv[0];
1542 *p++ = '-';
1543
1544 i = strlen(buf);
1545 if (i > LastArgv - p - 2) {
1546 i = LastArgv - p - 2;
1547 buf[i] = '\0';
1548 }
1549 bp = buf;
1550 while (ch = *bp++)
1551 if (ch != '\n' && ch != '\r')
1552 *p++ = ch;
1553 while (p < LastArgv)
1554 *p++ = ' ';
1555}
1556#endif /* DOTITLE */
1557
1558VOIDRET catchexit FUNCTION_NOARGS
1559{
1560 closelog();
1561}
1562
1563int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1564{
1565 int addrlen, on = 1;
1566 char *cp;
1567#ifdef IP_TOS
1568 int tos;
1569#endif /* IP_TOS */
1570
1571 {
1572 int i;
1573
1574 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1575 close(i);
1576 }
1577
1578 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1579 anonymous ftp's that chroot and can't do it later. */
1580 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1581 atexit(catchexit);
1582 addrlen = sizeof(his_addr);
1583 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1584 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1585 exit(1);
1586 }
1587 addrlen = sizeof(ctrl_addr);
1588 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1589 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1590 exit(1);
1591 }
1592#ifdef IP_TOS
1593 tos = IPTOS_LOWDELAY;
1594 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1595 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1596#endif
1597 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1598 debug = 0;
1599#if DOTITLE
1600 /* Save start and extent of argv for setproctitle. */
1601 Argv = argv;
1602 while (*envp)
1603 envp++;
1604 LastArgv = envp[-1] + strlen(envp[-1]);
1605#endif /* DOTITLE */
1606
1607 argc--, argv++;
1608 while (argc > 0 && *argv[0] == '-') {
1609 for (cp = &argv[0][1]; *cp; cp++)
1610 switch (*cp) {
1611
1612 case 'v':
1613 debug = 1;
1614 break;
1615
1616 case 'd':
1617 debug = 1;
1618 break;
1619
1620 case 'l':
1621 break;
1622
1623 case 't':
1624 timeout = atoi(++cp);
1625 if (maxtimeout < timeout)
1626 maxtimeout = timeout;
1627 goto nextopt;
1628
1629 case 'T':
1630 maxtimeout = atoi(++cp);
1631 if (timeout > maxtimeout)
1632 timeout = maxtimeout;
1633 goto nextopt;
1634
1635 case 'u':
1636 {
1637 int val = 0;
1638
1639 while (*++cp && *cp >= '0' && *cp <= '9')
1640 val = val * 8 + *cp - '0';
1641 if (*cp)
1642 fprintf(stderr, "ftpd: Bad value for -u\n");
1643 else
1644 defumask = val;
1645 goto nextopt;
1646 }
1647
1648 default:
1649 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1650 *cp);
1651 break;
1652 }
1653nextopt:
1654 argc--, argv++;
1655 }
1656 freopen(_PATH_DEVNULL, "w", stderr);
1657 signal(SIGCHLD, SIG_IGN);
1658 enable_signalling();
1659
1660 /* Try to handle urgent data inline */
1661#ifdef SO_OOBINLINE
1662 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1663 syslog(LOG_ERR, "setsockopt: %m");
1664#endif
1665
1666#ifdef F_SETOWN
1667 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1668 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1669#endif
1670 dolog(&his_addr);
1671 /* Set up default state */
1672 data = -1;
1673 type = TYPE_A;
1674 form = FORM_N;
1675 stru = STRU_F;
1676 mode = MODE_S;
1677 tmpline[0] = '\0';
1678 af_pwok = opieaccessfile(remotehost);
1679
1680 {
1681 FILE *fd;
1682 char line[128];
1683
1684 /* If logins are disabled, print out the message. */
1685 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1686 while (fgets(line, sizeof(line), fd) != NULL) {
1687 if ((cp = strchr(line, '\n')) != NULL)
1688 *cp = '\0';
1689 lreply(530, "%s", line);
1690 }
1691 (void) fflush(stdout);
1692 (void) fclose(fd);
1693 reply(530, "System not available.");
1694 exit(0);
1695 }
1696 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1697 while (fgets(line, sizeof(line), fd) != NULL) {
1698 if ((cp = strchr(line, '\n')) != NULL)
1699 *cp = '\0';
1700 lreply(220, "%s", line);
1701 }
1702 (void) fflush(stdout);
1703 (void) fclose(fd);
1704 /* reply(220,) must follow */
1705 }
1706 };
1707
1708 reply(220, "FTP server ready.");
1709
1710 setjmp(errcatch);
1711 for (;;)
1712 yyparse();
1713 /* NOTREACHED */
1714 return 0;
1715}