1220422Sgabor/* opieftpd.c: Main program for an FTP daemon.
2220422Sgabor
3210389Sgabor%%% portions-copyright-cmetz-96
4210389SgaborPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5210389SgaborReserved. The Inner Net License Version 2 applies to these portions of
6211496Sdesthe software.
7210389SgaborYou should have received a copy of the license with this software. If
8210389Sgaboryou didn't get a copy, you may request one from <license@inner.net>.
9210389Sgabor
10210389SgaborPortions of this software are Copyright 1995 by Randall Atkinson and Dan
11210389SgaborMcDonald, All Rights Reserved. All Rights under this copyright are assigned
12210389Sgaborto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13210389SgaborLicense Agreement applies to this software.
14210389Sgabor
15210389Sgabor	History:
16210389Sgabor
17210389Sgabor	Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18210389Sgabor		opiestrncpy(). Fix incorrect use of setproctitle().
19210389Sgabor	Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20210389Sgabor		done already (and conditionally) in opie_cfg.h.
21210389Sgabor	Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22210389Sgabor		Merged in a security fix to BSD-derived ftpds.
23210389Sgabor	Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24210389Sgabor		Moved LS_COMMAND here.
25210389Sgabor	Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26210389Sgabor                Removed useless strings (I don't think that removing the
27210389Sgabor                ucb copyright one is a problem -- please let me know if
28210389Sgabor                I'm wrong). Changed default CMASK to 077. Removed random
29210389Sgabor                comments. Use ANSI stdargs for reply/lreply if we can,
30210389Sgabor                added stdargs version of reply/lreply. Don't declare the
31210389Sgabor                tos variable unless IP_TOS defined. Include stdargs headers
32210389Sgabor                early. More headers ifdefed. Made everything static.
33210389Sgabor                Got rid of gethostname() call and use of hostname. Pared
34210389Sgabor                down status response for places where header files frequently
35210389Sgabor                cause trouble. Made logging of user logins (ala -l)
36210389Sgabor                non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37210389Sgabor	Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38210389Sgabor	        result of opiechallenge (allows access control to work).
39210389Sgabor		Added patches for AIX. Symbol changes for autoconf.
40210389Sgabor        Modified at NRL for OPIE 2.01. Changed password lookup handling
41210389Sgabor                to avoid problems with drain-bamaged shadow password packages.
42210389Sgabor                Properly handle internal state for anonymous FTP. Unlock
43210389Sgabor                user accounts properly if login fails because of /etc/shells.
44210578Sgabor                Make sure to close syslog by function to avoid problems with
45210389Sgabor                drain bamaged syslog implementations.
46210389Sgabor	Modified at NRL for OPIE 2.0.
47210389Sgabor	Originally from BSD Net/2.
48210389Sgabor
49210389Sgabor	        There is some really, really ugly code in here.
50210389Sgabor
51210389Sgabor$FreeBSD$
52210389Sgabor*/
53210389Sgabor/*
54210389Sgabor * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55210389Sgabor * All rights reserved.
56210389Sgabor *
57210578Sgabor * Redistribution and use in source and binary forms, with or without
58210578Sgabor * modification, are permitted provided that the following conditions
59210578Sgabor * are met:
60220421Sgabor * 1. Redistributions of source code must retain the above copyright
61210578Sgabor *    notice, this list of conditions and the following disclaimer.
62210578Sgabor * 2. Redistributions in binary form must reproduce the above copyright
63210578Sgabor *    notice, this list of conditions and the following disclaimer in the
64220421Sgabor *    documentation and/or other materials provided with the distribution.
65210578Sgabor * 3. All advertising materials mentioning features or use of this software
66210578Sgabor *    must display the following acknowledgement:
67220421Sgabor *      This product includes software developed by the University of
68220421Sgabor *      California, Berkeley and its contributors.
69210578Sgabor * 4. Neither the name of the University nor the names of its contributors
70210578Sgabor *    may be used to endorse or promote products derived from this software
71210578Sgabor *    without specific prior written permission.
72210578Sgabor *
73210578Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74210578Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75210578Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76210578Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77210578Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78211364Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79210578Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80210578Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81210578Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82210578Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83210578Sgabor * SUCH DAMAGE.
84210578Sgabor */
85210578Sgabor
86210578Sgabor#include "opie_cfg.h"
87210578Sgabor
88210578Sgabor#if HAVE_ANSISTDARG
89210578Sgabor#include <stdarg.h>
90210578Sgabor#endif /* HAVE_ANSISTDARG */
91210578Sgabor
92210578Sgabor/*
93210578Sgabor * FTP server.
94210578Sgabor */
95210578Sgabor
96210578Sgabor#if HAVE_SYS_PARAM_H
97210389Sgabor#include <sys/param.h>
98210389Sgabor#endif /* HAVE_SYS_PARAM_H */
99210389Sgabor#include <sys/stat.h>
100210389Sgabor/* #include <sys/ioctl.h> */
101210389Sgabor#include <sys/socket.h>
102210389Sgabor#include <sys/wait.h>
103210389Sgabor#ifdef SYS_FCNTL_H
104210389Sgabor#include <sys/fcntl.h>
105210389Sgabor#else
106210430Sdelphij#include <fcntl.h>
107210389Sgabor#endif	/* SYS_FCNTL_H */
108210389Sgabor#include <sys/types.h>
109210389Sgabor
110210389Sgabor#include <netinet/in.h>
111210389Sgabor#include <netinet/in_systm.h>
112210389Sgabor#include <netinet/ip.h>
113210389Sgabor
114210389Sgabor#define	FTP_NAMES
115210389Sgabor#include <arpa/ftp.h>
116210389Sgabor#include <arpa/inet.h>
117210389Sgabor#include <arpa/telnet.h>
118210389Sgabor
119210389Sgabor#include <signal.h>
120210389Sgabor#include <fcntl.h>
121210389Sgabor#if HAVE_TIME_H
122210389Sgabor#include <time.h>
123210389Sgabor#endif /* HAVE_TIME_H */
124210389Sgabor#if HAVE_PWD_H
125210389Sgabor#include <pwd.h>
126210389Sgabor#endif /* HAVE_PWD_H */
127210430Sdelphij#include <setjmp.h>
128210389Sgabor#include <netdb.h>
129210389Sgabor#include <errno.h>
130210389Sgabor#include <syslog.h>
131210389Sgabor#if HAVE_UNISTD_H
132210389Sgabor#include <unistd.h>
133210389Sgabor#endif /* HAVE_UNISTD_H */
134210389Sgabor#include <stdio.h>
135210389Sgabor#include <ctype.h>
136210389Sgabor#include <stdlib.h>
137210389Sgabor#include <string.h>
138210389Sgabor#include <grp.h>
139210389Sgabor
140210389Sgabor#include "opie.h"
141210389Sgabor
142210389Sgabor#if HAVE_SHADOW_H
143210389Sgabor#include <shadow.h>
144210389Sgabor#endif /* HAVE_SHADOW_H */
145210389Sgabor
146210389Sgabor#if HAVE_CRYPT_H
147210578Sgabor#include <crypt.h>
148210430Sdelphij#endif /* HAVE_CRYPT_H */
149210430Sdelphij
150210430Sdelphij#if HAVE_SYS_UTSNAME_H
151211364Sgabor#include <sys/utsname.h>
152211364Sgabor#endif /* HAVE_SYS_UTSNAME_H */
153211364Sgabor
154210430Sdelphij#ifdef _AIX
155210578Sgabor#include <sys/id.h>
156210430Sdelphij#include <sys/priv.h>
157210430Sdelphij#endif /* _AIX */
158210389Sgabor
159210578Sgabor#ifdef IP_TOS
160210578Sgabor#ifndef IPTOS_THROUGHPUT
161210389Sgabor#undef IP_TOS
162210389Sgabor#endif /* !IPTOS_THROUGHPUT */
163210389Sgabor#ifndef IPTOS_LOWDELAY
164210389Sgabor#undef IP_TOS
165210389Sgabor#endif /* !IPTOS_LOWDELAY */
166210389Sgabor#endif /* IP_TOS */
167210389Sgabor
168210430Sdelphijextern int errno;
169210389Sgaborextern char *home;	/* pointer to home directory for glob */
170210389Sgaborextern FILE *ftpd_popen __P((char *, char *));
171210389Sgaborextern int ftpd_pclose __P((FILE *));
172210389Sgaborextern char cbuf[];
173210389Sgaborextern off_t restart_point;
174210389Sgabor
175210389Sgaborstatic struct sockaddr_in ctrl_addr;
176210389Sgaborstatic struct sockaddr_in data_source;
177210389Sgaborstruct sockaddr_in data_dest;
178210389Sgaborstruct sockaddr_in his_addr;
179210389Sgaborstatic struct sockaddr_in pasv_addr;
180210389Sgabor
181210389Sgaborstatic int data;
182210389Sgaborjmp_buf errcatch;
183210389Sgaborstatic jmp_buf urgcatch;
184210389Sgaborint logged_in;
185210389Sgaborstruct passwd *pw;
186210389Sgaborint debug;
187210389Sgaborint timeout = 900;	/* timeout after 15 minutes of inactivity */
188210389Sgaborint maxtimeout = 7200;	/* don't allow idle time to be set beyond 2 hours */
189210389Sgabor
190211463Sgabor#if DOANONYMOUS
191210389Sgaborstatic int guest;
192210389Sgabor#endif	/* DOANONYMOUS */
193210389Sgaborint type;
194210389Sgaborint form;
195210389Sgaborstatic int stru;	/* avoid C keyword */
196210389Sgaborstatic int mode;
197210389Sgaborint usedefault = 1;	/* for data transfers */
198210389Sgaborint pdata = -1;	/* for passive mode */
199210389Sgaborstatic int transflag;
200210389Sgaborstatic off_t file_size;
201210389Sgaborstatic off_t byte_count;
202210389Sgabor
203210389Sgabor#if (!defined(CMASK) || CMASK == 0)
204210389Sgabor#undef CMASK
205210389Sgabor#define CMASK 077
206210389Sgabor#endif
207210389Sgabor
208210389Sgaborstatic int defumask = CMASK;	/* default umask value */
209210389Sgaborchar tmpline[7];
210210389Sgaborchar remotehost[MAXHOSTNAMELEN];
211210389Sgabor
212210389Sgabor/*
213210389Sgabor * Timeout intervals for retrying connections
214210389Sgabor * to hosts that don't accept PORT cmds.  This
215210389Sgabor * is a kludge, but given the problems with TCP...
216210389Sgabor */
217210389Sgabor#define	SWAITMAX	90	/* wait at most 90 seconds */
218210389Sgabor#define	SWAITINT	5	/* interval between retries */
219210389Sgabor
220210389Sgaborstatic int swaitmax = SWAITMAX;
221211463Sgaborstatic int swaitint = SWAITINT;
222210389Sgabor
223210389Sgabor#if DOTITLE
224210389Sgaborstatic char **Argv = NULL;	/* pointer to argument vector */
225210389Sgaborstatic char *LastArgv = NULL;	/* end of argv */
226210389Sgaborstatic char proctitle[BUFSIZ];	/* initial part of title */
227210389Sgabor#endif	/* DOTITLE */
228210389Sgabor
229210389Sgaborstatic int af_pwok = 0, pwok = 0;
230210389Sgaborstatic struct opie opiestate;
231210389Sgabor
232210389SgaborVOIDRET perror_reply __P((int, char *));
233210389SgaborVOIDRET dologout __P((int));
234210430Sdelphijchar *getline __P((char *, int, FILE *));
235210389SgaborVOIDRET upper __P((char *));
236210389Sgabor
237210389Sgaborstatic VOIDRET lostconn __P((int));
238210389Sgaborstatic VOIDRET myoob __P((int));
239210389Sgaborstatic FILE *getdatasock __P((char *));
240210389Sgaborstatic FILE *dataconn __P((char *, off_t, char *));
241210389Sgaborstatic int checkuser __P((char *));
242210389Sgaborstatic VOIDRET end_login __P((void));
243210389Sgaborstatic VOIDRET send_data __P((FILE *, FILE *, off_t));
244210389Sgaborstatic int receive_data __P((FILE *, FILE *));
245210389Sgaborstatic char *gunique __P((char *));
246210389Sgaborstatic char *sgetsave __P((char *));
247210389Sgabor
248210389Sgaborint opielogwtmp __P((char *, char *, char *, char *));
249210389Sgabor
250210389Sgaborint fclose __P((FILE *));
251210389Sgabor
252210389Sgabor#ifdef HAVE_ANSISTDARG
253210389SgaborVOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
254210389Sgabor{
255210389Sgabor  va_list ap;
256210389Sgabor  char buffer[1024];
257210389Sgabor
258210389Sgabor  va_start(ap, fmt);
259210389Sgabor  vsprintf(buffer, fmt, ap);
260210389Sgabor  va_end(ap);
261210461Sgabor
262210389Sgabor  printf("%d %s\r\n", n, buffer);
263210461Sgabor  fflush(stdout);
264210389Sgabor  if (debug)
265210389Sgabor    syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
266210389Sgabor}
267210622Sgabor#else /* HAVE_ANSISTDARG */
268210389SgaborVOIDRET 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)
269210430Sdelphij{
270210389Sgabor  printf("%d ", n);
271210389Sgabor  printf(fmt, p0, p1, p2, p3, p4, p5);
272210389Sgabor  printf("\r\n");
273210389Sgabor  fflush(stdout);
274210389Sgabor  if (debug) {
275210389Sgabor    syslog(LOG_DEBUG, "<--- %d ", n);
276210389Sgabor    syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
277210389Sgabor  }
278210389Sgabor}
279210389Sgabor#endif /* HAVE_ANSISTDARG */
280210389Sgabor
281210389Sgabor#ifdef HAVE_ANSISTDARG
282210389SgaborVOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
283220421Sgabor{
284210389Sgabor  va_list ap;
285210389Sgabor  char buffer[1024];
286210389Sgabor
287210389Sgabor  va_start(ap, fmt);
288210389Sgabor  vsprintf(buffer, fmt, ap);
289210389Sgabor  va_end(ap);
290210389Sgabor
291210389Sgabor  printf("%d- %s\r\n", n, buffer);
292210389Sgabor  fflush(stdout);
293210389Sgabor  if (debug)
294210389Sgabor    syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
295210389Sgabor}
296210389Sgabor#else /* HAVE_ANSISTDARG */
297210389SgaborVOIDRET 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)
298210389Sgabor{
299210389Sgabor  printf("%d- ", n);
300210389Sgabor  printf(fmt, p0, p1, p2, p3, p4, p5);
301210389Sgabor  printf("\r\n");
302210389Sgabor  fflush(stdout);
303210389Sgabor  if (debug) {
304210389Sgabor    syslog(LOG_DEBUG, "<--- %d- ", n);
305210389Sgabor    syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
306210389Sgabor  }
307210389Sgabor}
308210389Sgabor#endif /* HAVE_ANSISTDARG */
309210389Sgabor
310210389SgaborVOIDRET enable_signalling FUNCTION_NOARGS
311210389Sgabor{
312210389Sgabor	signal(SIGPIPE, lostconn);
313210389Sgabor	if ((int)signal(SIGURG, myoob) < 0)
314210389Sgabor		syslog(LOG_ERR, "signal: %m");
315210389Sgabor}
316210389Sgabor
317210389SgaborVOIDRET disable_signalling FUNCTION_NOARGS
318210389Sgabor{
319210389Sgabor	signal(SIGPIPE, SIG_IGN);
320210389Sgabor	if ((int)signal(SIGURG, SIG_IGN) < 0)
321210389Sgabor		syslog(LOG_ERR, "signal: %m");
322210389Sgabor}
323210389Sgabor
324220421Sgaborstatic VOIDRET lostconn FUNCTION((input), int input)
325220421Sgabor{
326211364Sgabor  if (debug)
327210389Sgabor    syslog(LOG_DEBUG, "lost connection");
328211364Sgabor  dologout(-1);
329211364Sgabor}
330211364Sgabor
331211364Sgaborstatic char ttyline[20];
332210389Sgabor
333211364Sgabor/*
334211364Sgabor * Helper function for sgetpwnam().
335211364Sgabor */
336211364Sgaborstatic char *sgetsave FUNCTION((s), char *s)
337211364Sgabor{
338211364Sgabor  char *new = malloc((unsigned) strlen(s) + 1);
339210389Sgabor
340210389Sgabor  if (new == NULL) {
341210389Sgabor    perror_reply(421, "Local resource failure: malloc");
342210389Sgabor    dologout(1);
343210389Sgabor    /* NOTREACHED */
344210389Sgabor  }
345210389Sgabor  strcpy(new, s);
346210461Sgabor  return (new);
347210461Sgabor}
348210389Sgabor
349210389Sgabor/*
350210389Sgabor * Save the result of a getpwnam.  Used for USER command, since
351210389Sgabor * the data returned must not be clobbered by any other command
352210389Sgabor * (e.g., globbing).
353210389Sgabor */
354210389Sgaborstatic struct passwd *sgetpwnam FUNCTION((name), char *name)
355210389Sgabor{
356210461Sgabor  static struct passwd save;
357210389Sgabor  register struct passwd *p;
358210389Sgabor
359210389Sgabor#if HAVE_SHADOW
360210389Sgabor  struct spwd *spwd;
361210389Sgabor#endif /* HAVE_SHADOW */
362210389Sgabor
363210389Sgabor  if ((p = getpwnam(name)) == NULL)
364210389Sgabor    return (p);
365210389Sgabor
366210389Sgabor#if HAVE_SHADOW
367210389Sgabor  if ((spwd = getspnam(name)) == NULL)
368210389Sgabor    return NULL;
369210479Sgabor
370210389Sgabor  endspent();
371210389Sgabor
372210389Sgabor  p->pw_passwd = spwd->sp_pwdp;
373210389Sgabor#endif /* HAVE_SHADOW */
374210389Sgabor
375210389Sgabor  endpwent();
376210389Sgabor
377210389Sgabor  if (save.pw_name) {
378210389Sgabor    free(save.pw_name);
379210389Sgabor    free(save.pw_passwd);
380210389Sgabor    free(save.pw_gecos);
381210389Sgabor    free(save.pw_dir);
382210389Sgabor    free(save.pw_shell);
383210389Sgabor  }
384210389Sgabor  save = *p;
385210389Sgabor  save.pw_name = sgetsave(p->pw_name);
386210389Sgabor  save.pw_passwd = sgetsave(p->pw_passwd);
387210389Sgabor  save.pw_gecos = sgetsave(p->pw_gecos);
388210389Sgabor  save.pw_dir = sgetsave(p->pw_dir);
389210389Sgabor  save.pw_shell = sgetsave(p->pw_shell);
390210389Sgabor  return (&save);
391210389Sgabor}
392210389Sgabor
393210389Sgaborint login_attempts;	/* number of failed login attempts */
394210389Sgaborint askpasswd;	/* had user command, ask for passwd */
395210389Sgabor
396210389Sgabor/*
397210389Sgabor * USER command.
398210389Sgabor * Sets global passwd pointer pw if named account exists and is acceptable;
399210389Sgabor * sets askpasswd if a PASS command is expected.  If logged in previously,
400210389Sgabor * need to reset state.  If name is "ftp" or "anonymous", the name is not in
401210389Sgabor * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402210389Sgabor * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
403210389Sgabor * requesting login privileges.  Disallow anyone who does not have a standard
404210389Sgabor * shell as returned by getusershell().  Disallow anyone mentioned in the file
405210389Sgabor * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406210389Sgabor */
407210389Sgaborint user FUNCTION((name), char *name)
408210389Sgabor{
409210389Sgabor  register char *cp;
410210389Sgabor  char *shell;
411210389Sgabor
412210389Sgabor  if (logged_in) {
413210389Sgabor#if DOANONYMOUS
414210389Sgabor    if (guest) {
415210389Sgabor      reply(530, "Can't change user from guest login.");
416210389Sgabor      return -1;
417210389Sgabor    }
418210389Sgabor#endif	/* DOANONMOUS */
419210389Sgabor    end_login();
420210389Sgabor  }
421210389Sgabor  askpasswd = 1;
422210389Sgabor#if DOANONYMOUS
423210389Sgabor  guest = 0;
424210389Sgabor  if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
425210389Sgabor    if (!checkuser("ftp") && !checkuser("anonymous"))
426210389Sgabor      if ((pw = sgetpwnam("ftp")) != NULL) {
427210389Sgabor	guest = 1;
428210389Sgabor	askpasswd = 1;
429210389Sgabor	reply(331, "Guest login ok, send your e-mail address as your password.");
430210389Sgabor	syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
431210389Sgabor        return 0;
432210389Sgabor      }
433210389Sgabor#endif	/* DOANONYMOUS */
434210389Sgabor  if (pw = sgetpwnam(name)) {
435210578Sgabor    if ((shell = pw->pw_shell) == NULL || *shell == 0)
436210578Sgabor      shell = _PATH_BSHELL;
437210578Sgabor    while ((cp = getusershell()) != NULL)
438210578Sgabor      if (!strcmp(cp, shell))
439210578Sgabor	break;
440210578Sgabor    endusershell();
441210578Sgabor    if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
442210578Sgabor#if DEBUG
443210578Sgabor      if (!cp)
444210578Sgabor        syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
445210578Sgabor      if (checkuser(name))
446210578Sgabor        syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
447210578Sgabor      if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
448210389Sgabor        syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
449210389Sgabor#endif /* DEBUG */
450210389Sgabor      pw = (struct passwd *) NULL;
451210389Sgabor      askpasswd = -1;
452210389Sgabor    }
453210389Sgabor  }
454210389Sgabor  {
455210389Sgabor    char prompt[OPIE_CHALLENGE_MAX + 1];
456210389Sgabor
457210389Sgabor    opiechallenge(&opiestate, name, prompt);
458210389Sgabor
459210389Sgabor    if (askpasswd == -1) {
460210389Sgabor      syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
461210389Sgabor      pwok = 0;
462210389Sgabor    } else
463210389Sgabor      pwok = af_pwok && opiealways(pw->pw_dir);
464210389Sgabor
465210389Sgabor#if NEW_PROMPTS
466210389Sgabor    reply(331, "Response to %s %s for %s.", prompt,
467210389Sgabor#else /* NEW_PROMPTS */
468210389Sgabor    reply(331, "OTP response %s %s for %s.", prompt,
469210389Sgabor#endif /* NEW_PROMPTS */
470210389Sgabor	  pwok ? "requested" : "required", name);
471210389Sgabor  }
472210389Sgabor  /* Delay before reading passwd after first failed attempt to slow down
473210389Sgabor     passwd-guessing programs. */
474210389Sgabor  if (login_attempts)
475210389Sgabor    sleep((unsigned) login_attempts);
476210389Sgabor
477210389Sgabor  return 0;
478210389Sgabor}
479210389Sgabor
480210389Sgabor/*
481210389Sgabor * Check if a user is in the file _PATH_FTPUSERS
482210389Sgabor */
483210389Sgaborstatic int checkuser FUNCTION((name), char *name)
484210389Sgabor{
485210389Sgabor  register FILE *fd;
486210389Sgabor  register char *p;
487210389Sgabor  char line[BUFSIZ];
488210389Sgabor
489210389Sgabor  if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
490210389Sgabor    while (fgets(line, sizeof(line), fd) != NULL)
491210389Sgabor      if ((p = strchr(line, '\n')) != NULL) {
492210389Sgabor	*p = '\0';
493210389Sgabor	if (line[0] == '#')
494210389Sgabor	  continue;
495210389Sgabor	if (!strcmp(line, name)) {
496210389Sgabor          fclose(fd);
497210389Sgabor	  return (1);
498210389Sgabor        }
499210389Sgabor      }
500210389Sgabor    fclose(fd);
501210389Sgabor  }
502210389Sgabor  return (0);
503210389Sgabor}
504210389Sgabor
505210389Sgabor/*
506210389Sgabor * 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)
515    opielogwtmp(ttyline, "", "", "ftp");
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());
569  opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
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
636    setproctitle("%s: anonymous/%.*s", remotehost,
637            sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
638	    passwd);
639#endif /* 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
648    setproctitle("%s: %s", remotehost, pw->pw_name);
649#endif /* DOTITLE */
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)
1260    opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1261  else
1262    opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1263#if DOTITLE
1264  setproctitle("%s: connected", remotehost);
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");
1282    opielogwtmp(ttyline, "", "", "ftp");
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}
1716