opieftpd.c revision 39012
122347Spst/* opieftpd.c: Main program for an FTP daemon.
222347Spst
329964Sache%%% portions-copyright-cmetz-96
429964SachePortions of this software are Copyright 1996-1997 by Craig Metz, All Rights
522347SpstReserved. The Inner Net License Version 2 applies to these portions of
622347Spstthe software.
722347SpstYou should have received a copy of the license with this software. If
822347Spstyou didn't get a copy, you may request one from <license@inner.net>.
922347Spst
1022347SpstPortions of this software are Copyright 1995 by Randall Atkinson and Dan
1122347SpstMcDonald, All Rights Reserved. All Rights under this copyright are assigned
1222347Spstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
1322347SpstLicense Agreement applies to this software.
1422347Spst
1522347Spst	History:
1622347Spst
1729964Sache	Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
1829964Sache		Merged in a security fix to BSD-derived ftpds.
1922347Spst	Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
2022347Spst		Moved LS_COMMAND here.
2122347Spst	Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
2222347Spst                Removed useless strings (I don't think that removing the
2322347Spst                ucb copyright one is a problem -- please let me know if
2422347Spst                I'm wrong). Changed default CMASK to 077. Removed random
2522347Spst                comments. Use ANSI stdargs for reply/lreply if we can,
2622347Spst                added stdargs version of reply/lreply. Don't declare the
2722347Spst                tos variable unless IP_TOS defined. Include stdargs headers
2822347Spst                early. More headers ifdefed. Made everything static.
2922347Spst                Got rid of gethostname() call and use of hostname. Pared
3022347Spst                down status response for places where header files frequently
3122347Spst                cause trouble. Made logging of user logins (ala -l)
3222347Spst                non-optional. Moved reply()/lrepy(). Fixed some prototypes.
3322347Spst	Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
3422347Spst	        result of opiechallenge (allows access control to work).
3522347Spst		Added patches for AIX. Symbol changes for autoconf.
3622347Spst        Modified at NRL for OPIE 2.01. Changed password lookup handling
3722347Spst                to avoid problems with drain-bamaged shadow password packages.
3822347Spst                Properly handle internal state for anonymous FTP. Unlock
3922347Spst                user accounts properly if login fails because of /etc/shells.
4022347Spst                Make sure to close syslog by function to avoid problems with
4122347Spst                drain bamaged syslog implementations.
4222347Spst	Modified at NRL for OPIE 2.0.
4322347Spst	Originally from BSD Net/2.
4422347Spst
4522347Spst	        There is some really, really ugly code in here.
4622347Spst*/
4722347Spst/*
4822347Spst * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
4922347Spst * All rights reserved.
5022347Spst *
5122347Spst * Redistribution and use in source and binary forms, with or without
5222347Spst * modification, are permitted provided that the following conditions
5322347Spst * are met:
5422347Spst * 1. Redistributions of source code must retain the above copyright
5522347Spst *    notice, this list of conditions and the following disclaimer.
5622347Spst * 2. Redistributions in binary form must reproduce the above copyright
5722347Spst *    notice, this list of conditions and the following disclaimer in the
5822347Spst *    documentation and/or other materials provided with the distribution.
5922347Spst * 3. All advertising materials mentioning features or use of this software
6022347Spst *    must display the following acknowledgement:
6122347Spst *      This product includes software developed by the University of
6222347Spst *      California, Berkeley and its contributors.
6322347Spst * 4. Neither the name of the University nor the names of its contributors
6422347Spst *    may be used to endorse or promote products derived from this software
6522347Spst *    without specific prior written permission.
6622347Spst *
6722347Spst * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6822347Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6922347Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
7022347Spst * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
7122347Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7222347Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
7322347Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
7422347Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
7522347Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
7622347Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
7722347Spst * SUCH DAMAGE.
7822347Spst */
7922347Spst
8022347Spst#include "opie_cfg.h"
8122347Spst
8222347Spst#if HAVE_ANSISTDARG
8322347Spst#include <stdarg.h>
8422347Spst#endif /* HAVE_ANSISTDARG */
8522347Spst
8622347Spst/*
8722347Spst * FTP server.
8822347Spst */
8922347Spst
9022347Spst#if HAVE_SYS_PARAM_H
9122347Spst#include <sys/param.h>
9222347Spst#endif /* HAVE_SYS_PARAM_H */
9322347Spst#include <sys/stat.h>
9422347Spst/* #include <sys/ioctl.h> */
9522347Spst#include <sys/socket.h>
9622347Spst#include <sys/wait.h>
9722347Spst#ifdef SYS_FCNTL_H
9822347Spst#include <sys/fcntl.h>
9922347Spst#else
10022347Spst#include <fcntl.h>
10122347Spst#endif	/* SYS_FCNTL_H */
10222347Spst#include <sys/types.h>
10322347Spst
10422347Spst#include <netinet/in.h>
10522347Spst#include <netinet/in_systm.h>
10622347Spst#include <netinet/ip.h>
10722347Spst
10822347Spst#define	FTP_NAMES
10922347Spst#include <arpa/ftp.h>
11022347Spst#include <arpa/inet.h>
11122347Spst#include <arpa/telnet.h>
11222347Spst
11322347Spst#include <signal.h>
11422347Spst#include <dirent.h>
11522347Spst#include <fcntl.h>
11622347Spst#if HAVE_TIME_H
11722347Spst#include <time.h>
11822347Spst#endif /* HAVE_TIME_H */
11922347Spst#if HAVE_PWD_H
12022347Spst#include <pwd.h>
12122347Spst#endif /* HAVE_PWD_H */
12222347Spst#include <setjmp.h>
12322347Spst#include <netdb.h>
12422347Spst#include <errno.h>
12522347Spst#include <syslog.h>
12622347Spst#if HAVE_UNISTD_H
12722347Spst#include <unistd.h>
12822347Spst#endif /* HAVE_UNISTD_H */
12922347Spst#include <stdio.h>
13022347Spst#include <ctype.h>
13122347Spst#include <stdlib.h>
13222347Spst#include <string.h>
13322347Spst#include <grp.h>
13422347Spst
13522347Spst#include "opie.h"
13622347Spst
13722347Spst#if HAVE_SHADOW_H
13822347Spst#include <shadow.h>
13922347Spst#endif /* HAVE_SHADOW_H */
14022347Spst
14122347Spst#if HAVE_CRYPT_H
14222347Spst#include <crypt.h>
14322347Spst#endif /* HAVE_CRYPT_H */
14422347Spst
14522347Spst#if HAVE_SYS_UTSNAME_H
14622347Spst#include <sys/utsname.h>
14722347Spst#endif /* HAVE_SYS_UTSNAME_H */
14822347Spst
14922347Spst#ifdef _AIX
15022347Spst#include <sys/id.h>
15122347Spst#include <sys/priv.h>
15222347Spst#endif /* _AIX */
15322347Spst
15422347Spst#ifdef IP_TOS
15522347Spst#ifndef IPTOS_THROUGHPUT
15622347Spst#undef IP_TOS
15722347Spst#endif /* !IPTOS_THROUGHPUT */
15822347Spst#ifndef IPTOS_LOWDELAY
15922347Spst#undef IP_TOS
16022347Spst#endif /* !IPTOS_LOWDELAY */
16122347Spst#endif /* IP_TOS */
16222347Spst
16322347Spstextern int errno;
16422347Spstextern char *home;	/* pointer to home directory for glob */
16522347Spstextern FILE *ftpd_popen __P((char *, char *));
16622347Spstextern int ftpd_pclose __P((FILE *));
16722347Spstextern char cbuf[];
16822347Spstextern off_t restart_point;
16922347Spst
17022347Spststatic struct sockaddr_in ctrl_addr;
17122347Spststatic struct sockaddr_in data_source;
17222347Spststruct sockaddr_in data_dest;
17322347Spststruct sockaddr_in his_addr;
17422347Spststatic struct sockaddr_in pasv_addr;
17522347Spst
17622347Spststatic int data;
17722347Spstjmp_buf errcatch;
17822347Spststatic jmp_buf urgcatch;
17922347Spstint logged_in;
18022347Spststruct passwd *pw;
18122347Spstint debug;
18222347Spstint timeout = 900;	/* timeout after 15 minutes of inactivity */
18322347Spstint maxtimeout = 7200;	/* don't allow idle time to be set beyond 2 hours */
18422347Spst
18522347Spst#if DOANONYMOUS
18622347Spststatic int guest;
18722347Spst#endif	/* DOANONYMOUS */
18822347Spstint type;
18922347Spstint form;
19022347Spststatic int stru;	/* avoid C keyword */
19122347Spststatic int mode;
19222347Spstint usedefault = 1;	/* for data transfers */
19322347Spstint pdata = -1;	/* for passive mode */
19422347Spststatic int transflag;
19522347Spststatic off_t file_size;
19622347Spststatic off_t byte_count;
19722347Spst
19822347Spst#if (!defined(CMASK) || CMASK == 0)
19922347Spst#undef CMASK
20022347Spst#define CMASK 077
20122347Spst#endif
20222347Spst
20322347Spststatic int defumask = CMASK;	/* default umask value */
20422347Spstchar tmpline[7];
20522347Spstchar remotehost[MAXHOSTNAMELEN];
20622347Spst
20722347Spst/*
20822347Spst * Timeout intervals for retrying connections
20922347Spst * to hosts that don't accept PORT cmds.  This
21022347Spst * is a kludge, but given the problems with TCP...
21122347Spst */
21222347Spst#define	SWAITMAX	90	/* wait at most 90 seconds */
21322347Spst#define	SWAITINT	5	/* interval between retries */
21422347Spst
21522347Spststatic int swaitmax = SWAITMAX;
21622347Spststatic int swaitint = SWAITINT;
21722347Spst
21822347Spst#if DOTITLE
21922347Spststatic char **Argv = NULL;	/* pointer to argument vector */
22022347Spststatic char *LastArgv = NULL;	/* end of argv */
22122347Spststatic char proctitle[BUFSIZ];	/* initial part of title */
22222347Spst#endif	/* DOTITLE */
22322347Spst
22422347Spststatic int af_pwok = 0, pwok = 0;
22522347Spststatic struct opie opiestate;
22622347Spst
22722347SpstVOIDRET perror_reply __P((int, char *));
22822347SpstVOIDRET dologout __P((int));
22922347Spstchar *getline __P((char *, int, FILE *));
23022347SpstVOIDRET upper __P((char *));
23122347Spst
23222347Spststatic VOIDRET lostconn __P((int));
23329964Sachestatic VOIDRET myoob __P((int));
23422347Spststatic FILE *getdatasock __P((char *));
23522347Spststatic FILE *dataconn __P((char *, off_t, char *));
23622347Spststatic int checkuser __P((char *));
23722347Spststatic VOIDRET end_login __P((void));
23822347Spststatic VOIDRET send_data __P((FILE *, FILE *, off_t));
23922347Spststatic int receive_data __P((FILE *, FILE *));
24022347Spststatic char *gunique __P((char *));
24122347Spststatic char *sgetsave __P((char *));
24222347Spst
24329964Sacheint opielogwtmp __P((char *, char *, char *));
24422347Spst
24522347Spstint fclose __P((FILE *));
24622347Spst
24722347Spst#ifdef HAVE_ANSISTDARG
24822347SpstVOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
24922347Spst{
25022347Spst  va_list ap;
25122347Spst  char buffer[1024];
25222347Spst
25322347Spst  va_start(ap, fmt);
25422347Spst  vsprintf(buffer, fmt, ap);
25522347Spst  va_end(ap);
25622347Spst
25722347Spst  printf("%d %s\r\n", n, buffer);
25822347Spst  fflush(stdout);
25922347Spst  if (debug)
26022347Spst    syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
26122347Spst}
26222347Spst#else /* HAVE_ANSISTDARG */
26322347SpstVOIDRET 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)
26422347Spst{
26522347Spst  printf("%d ", n);
26622347Spst  printf(fmt, p0, p1, p2, p3, p4, p5);
26722347Spst  printf("\r\n");
26822347Spst  fflush(stdout);
26922347Spst  if (debug) {
27022347Spst    syslog(LOG_DEBUG, "<--- %d ", n);
27122347Spst    syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
27222347Spst  }
27322347Spst}
27422347Spst#endif /* HAVE_ANSISTDARG */
27522347Spst
27622347Spst#ifdef HAVE_ANSISTDARG
27722347SpstVOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
27822347Spst{
27922347Spst  va_list ap;
28022347Spst  char buffer[1024];
28122347Spst
28222347Spst  va_start(ap, fmt);
28322347Spst  vsprintf(buffer, fmt, ap);
28422347Spst  va_end(ap);
28522347Spst
28622347Spst  printf("%d- %s\r\n", n, buffer);
28722347Spst  fflush(stdout);
28822347Spst  if (debug)
28922347Spst    syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
29022347Spst}
29122347Spst#else /* HAVE_ANSISTDARG */
29222347SpstVOIDRET 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)
29322347Spst{
29422347Spst  printf("%d- ", n);
29522347Spst  printf(fmt, p0, p1, p2, p3, p4, p5);
29622347Spst  printf("\r\n");
29722347Spst  fflush(stdout);
29822347Spst  if (debug) {
29922347Spst    syslog(LOG_DEBUG, "<--- %d- ", n);
30022347Spst    syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
30122347Spst  }
30222347Spst}
30322347Spst#endif /* HAVE_ANSISTDARG */
30422347Spst
30529964SacheVOIDRET enable_signalling FUNCTION_NOARGS
30629964Sache{
30729964Sache	signal(SIGPIPE, lostconn);
30829964Sache	if ((int)signal(SIGURG, myoob) < 0)
30929964Sache		syslog(LOG_ERR, "signal: %m");
31029964Sache}
31129964Sache
31229964SacheVOIDRET disable_signalling FUNCTION_NOARGS
31329964Sache{
31429964Sache	signal(SIGPIPE, SIG_IGN);
31529964Sache	if ((int)signal(SIGURG, SIG_IGN) < 0)
31629964Sache		syslog(LOG_ERR, "signal: %m");
31729964Sache}
31829964Sache
31922347Spststatic VOIDRET lostconn FUNCTION((input), int input)
32022347Spst{
32122347Spst  if (debug)
32222347Spst    syslog(LOG_DEBUG, "lost connection");
32322347Spst  dologout(-1);
32422347Spst}
32522347Spst
32622347Spststatic char ttyline[20];
32722347Spst
32822347Spst/*
32922347Spst * Helper function for sgetpwnam().
33022347Spst */
33122347Spststatic char *sgetsave FUNCTION((s), char *s)
33222347Spst{
33322347Spst  char *new = malloc((unsigned) strlen(s) + 1);
33422347Spst
33522347Spst  if (new == NULL) {
33622347Spst    perror_reply(421, "Local resource failure: malloc");
33722347Spst    dologout(1);
33822347Spst    /* NOTREACHED */
33922347Spst  }
34022347Spst  strcpy(new, s);
34122347Spst  return (new);
34222347Spst}
34322347Spst
34422347Spst/*
34522347Spst * Save the result of a getpwnam.  Used for USER command, since
34622347Spst * the data returned must not be clobbered by any other command
34722347Spst * (e.g., globbing).
34822347Spst */
34922347Spststatic struct passwd *sgetpwnam FUNCTION((name), char *name)
35022347Spst{
35122347Spst  static struct passwd save;
35222347Spst  register struct passwd *p;
35322347Spst
35422347Spst#if HAVE_SHADOW
35522347Spst  struct spwd *spwd;
35622347Spst#endif /* HAVE_SHADOW */
35722347Spst
35822347Spst  if ((p = getpwnam(name)) == NULL)
35922347Spst    return (p);
36022347Spst
36122347Spst#if HAVE_SHADOW
36222347Spst  if ((spwd = getspnam(name)) == NULL)
36322347Spst    return NULL;
36422347Spst
36522347Spst  endspent();
36622347Spst
36722347Spst  p->pw_passwd = spwd->sp_pwdp;
36822347Spst#endif /* HAVE_SHADOW */
36922347Spst
37022347Spst  endpwent();
37122347Spst
37222347Spst  if (save.pw_name) {
37322347Spst    free(save.pw_name);
37422347Spst    free(save.pw_passwd);
37522347Spst    free(save.pw_gecos);
37622347Spst    free(save.pw_dir);
37722347Spst    free(save.pw_shell);
37822347Spst  }
37922347Spst  save = *p;
38022347Spst  save.pw_name = sgetsave(p->pw_name);
38122347Spst  save.pw_passwd = sgetsave(p->pw_passwd);
38222347Spst  save.pw_gecos = sgetsave(p->pw_gecos);
38322347Spst  save.pw_dir = sgetsave(p->pw_dir);
38422347Spst  save.pw_shell = sgetsave(p->pw_shell);
38522347Spst  return (&save);
38622347Spst}
38722347Spst
38822347Spstint login_attempts;	/* number of failed login attempts */
38922347Spstint askpasswd;	/* had user command, ask for passwd */
39022347Spst
39122347Spst/*
39222347Spst * USER command.
39322347Spst * Sets global passwd pointer pw if named account exists and is acceptable;
39422347Spst * sets askpasswd if a PASS command is expected.  If logged in previously,
39522347Spst * need to reset state.  If name is "ftp" or "anonymous", the name is not in
39622347Spst * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
39722347Spst * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
39822347Spst * requesting login privileges.  Disallow anyone who does not have a standard
39922347Spst * shell as returned by getusershell().  Disallow anyone mentioned in the file
40022347Spst * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
40122347Spst */
40222347Spstint user FUNCTION((name), char *name)
40322347Spst{
40422347Spst  register char *cp;
40522347Spst  char *shell;
40622347Spst
40722347Spst  if (logged_in) {
40822347Spst#if DOANONYMOUS
40922347Spst    if (guest) {
41022347Spst      reply(530, "Can't change user from guest login.");
41122347Spst      return -1;
41222347Spst    }
41322347Spst#endif	/* DOANONMOUS */
41422347Spst    end_login();
41522347Spst  }
41622347Spst  askpasswd = 1;
41722347Spst#if DOANONYMOUS
41822347Spst  guest = 0;
41922347Spst  if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
42022347Spst    if (!checkuser("ftp") && !checkuser("anonymous"))
42122347Spst      if ((pw = sgetpwnam("ftp")) != NULL) {
42222347Spst	guest = 1;
42322347Spst	askpasswd = 1;
42429964Sache	reply(331, "Guest login ok, send your e-mail address as your password.");
42529964Sache	syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
42622347Spst        return 0;
42722347Spst      }
42822347Spst#endif	/* DOANONYMOUS */
42922347Spst  if (pw = sgetpwnam(name)) {
43022347Spst    if ((shell = pw->pw_shell) == NULL || *shell == 0)
43122347Spst      shell = _PATH_BSHELL;
43222347Spst    while ((cp = getusershell()) != NULL)
43322347Spst      if (!strcmp(cp, shell))
43422347Spst	break;
43522347Spst    endusershell();
43629964Sache    if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
43722347Spst#if DEBUG
43822347Spst      if (!cp)
43922347Spst        syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
44022347Spst      if (checkuser(name))
44122347Spst        syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
44222347Spst      if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
44322347Spst        syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
44422347Spst#endif /* DEBUG */
44522347Spst      pw = (struct passwd *) NULL;
44622347Spst      askpasswd = -1;
44722347Spst    }
44822347Spst  }
44922347Spst  {
45022347Spst    char prompt[OPIE_CHALLENGE_MAX + 1];
45122347Spst
45222347Spst    opiechallenge(&opiestate, name, prompt);
45322347Spst
45422347Spst    if (askpasswd == -1) {
45522347Spst      syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
45622347Spst      pwok = 0;
45722347Spst    } else
45822347Spst      pwok = af_pwok && opiealways(pw->pw_dir);
45922347Spst
46022347Spst#if NEW_PROMPTS
46122347Spst    reply(331, "Response to %s %s for %s.", prompt,
46222347Spst#else /* NEW_PROMPTS */
46322347Spst    reply(331, "OTP response %s %s for %s.", prompt,
46422347Spst#endif /* NEW_PROMPTS */
46522347Spst	  pwok ? "requested" : "required", name);
46622347Spst  }
46722347Spst  /* Delay before reading passwd after first failed attempt to slow down
46822347Spst     passwd-guessing programs. */
46922347Spst  if (login_attempts)
47022347Spst    sleep((unsigned) login_attempts);
47122347Spst
47222347Spst  return 0;
47322347Spst}
47422347Spst
47522347Spst/*
47622347Spst * Check if a user is in the file _PATH_FTPUSERS
47722347Spst */
47822347Spststatic int checkuser FUNCTION((name), char *name)
47922347Spst{
48022347Spst  register FILE *fd;
48122347Spst  register char *p;
48222347Spst  char line[BUFSIZ];
48322347Spst
48422347Spst  if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
48522347Spst    while (fgets(line, sizeof(line), fd) != NULL)
48622347Spst      if ((p = strchr(line, '\n')) != NULL) {
48722347Spst	*p = '\0';
48822347Spst	if (line[0] == '#')
48922347Spst	  continue;
49029964Sache	if (!strcmp(line, name)) {
49129964Sache          fclose(fd);
49222347Spst	  return (1);
49329964Sache        }
49422347Spst      }
49522347Spst    fclose(fd);
49622347Spst  }
49722347Spst  return (0);
49822347Spst}
49922347Spst
50022347Spst/*
50122347Spst * Terminate login as previous user, if any, resetting state;
50222347Spst * used when USER command is given or login fails.
50322347Spst */
50422347Spststatic VOIDRET end_login FUNCTION_NOARGS
50522347Spst{
50629964Sache  disable_signalling();
50722347Spst  if (seteuid((uid_t) 0))
50822347Spst    syslog(LOG_ERR, "Can't set euid");
50922347Spst  if (logged_in)
51029964Sache    opielogwtmp(ttyline, "", "");
51122347Spst  pw = NULL;
51222347Spst  logged_in = 0;
51322347Spst#if DOANONYMOUS
51422347Spst  guest = 0;
51522347Spst#endif	/* DOANONYMOUS */
51629964Sache  enable_signalling();
51722347Spst}
51822347Spst
51922347SpstVOIDRET pass FUNCTION((passwd), char *passwd)
52022347Spst{
52122347Spst  int legit = askpasswd + 1, i;
52222347Spst
52322347Spst  if (logged_in || askpasswd == 0) {
52422347Spst    reply(503, "Login with USER first.");
52522347Spst    return;
52622347Spst  }
52722347Spst  askpasswd = 0;
52822347Spst
52922347Spst#if DOANONYMOUS
53022347Spst  if (!guest) { /* "ftp" is only account allowed no password */
53122347Spst#endif	/* DOANONYMOUS */
53222347Spst    i = opieverify(&opiestate, passwd);
53322347Spst    if (legit && i && pwok)
53422347Spst      i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
53522347Spst    if (!legit || i) {
53622347Spst      reply(530, "Login incorrect.");
53722347Spst      pw = NULL;
53822347Spst      if (login_attempts++ >= 5) {
53922347Spst	syslog(LOG_WARNING,
54022347Spst	       "Repeated login failures for user %s from %s",
54122347Spst	       pw->pw_name, remotehost);
54222347Spst	exit(0);
54322347Spst      }
54422347Spst      return;
54522347Spst    }
54622347Spst#if DOANONYMOUS
54729964Sache  } else
54829964Sache    if ((passwd[0] <= ' ') ||  checkuser(passwd)) {
54929964Sache      reply(530, "No identity, no service.");
55029964Sache      syslog(LOG_DEBUG, "Bogus address: %s", passwd);
55129964Sache      exit(0);
55229964Sache    }
55322347Spst#endif	/* DOANONYMOUS */
55422347Spst  login_attempts = 0;	/* this time successful */
55529964Sache  if (setegid((gid_t) pw->pw_gid) < 0) {
55629964Sache    reply(550, "Can't set gid.");
55729964Sache    syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
55829964Sache    return;
55929964Sache  }
56022347Spst  initgroups(pw->pw_name, pw->pw_gid);
56122347Spst
56222347Spst  /* open wtmp before chroot */
56322347Spst  sprintf(ttyline, "ftp%d", getpid());
56429964Sache  opielogwtmp(ttyline, pw->pw_name, remotehost);
56522347Spst  logged_in = 1;
56622347Spst
56722347Spst#if DOANONYMOUS
56822347Spst  if (guest) {
56922347Spst    /* We MUST do a chdir() after the chroot. Otherwise the old current
57022347Spst       directory will be accessible as "." outside the new root! */
57122347Spst    if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
57222347Spst      reply(550, "Can't set guest privileges.");
57322347Spst      goto bad;
57422347Spst    }
57522347Spst  } else
57622347Spst#endif	/* DOANONYMOUS */
57722347Spst    if (chdir(pw->pw_dir) < 0) {
57822347Spst      if (chdir("/") < 0) {
57922347Spst	reply(530, "User %s: can't change directory to %s.",
58022347Spst	      pw->pw_name, pw->pw_dir);
58122347Spst	goto bad;
58222347Spst      } else
58322347Spst	lreply(230, "No directory! Logging in with home=/");
58422347Spst    }
58522347Spst/* This patch was contributed by an OPIE user. We don't know what it
58622347Spst   does, exactly. It may or may not work. */
58722347Spst#ifdef _AIX
58822347Spst   {
58922347Spst       priv_t priv;
59022347Spst       priv.pv_priv[0] = 0;
59122347Spst       priv.pv_priv[1] = 0;
59222347Spst       setgroups(NULL, NULL);
59322347Spst       if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
59422347Spst                   &priv, sizeof(priv_t)) < 0 ||
59522347Spst	   setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
59622347Spst           setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
59722347Spst           seteuid((uid_t)pw->pw_uid) < 0) {
59822347Spst               reply(550, "Can't set uid (_AIX3).");
59922347Spst               goto bad;
60022347Spst       }
60122347Spst    }
60222347Spst#else /* _AIX */
60322347Spst  if (seteuid((uid_t) pw->pw_uid) < 0) {
60422347Spst    reply(550, "Can't set uid.");
60522347Spst    goto bad;
60622347Spst  }
60722347Spst#endif /* _AIX */
60829964Sache /*
60929964Sache  * Display a login message, if it exists.
61029964Sache  * N.B. reply(230,) must follow the message.
61129964Sache  */
61229964Sache  {
61329964Sache  FILE *fd;
61429964Sache
61529964Sache  if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
61629964Sache    char *cp, line[128];
61729964Sache
61829964Sache    while (fgets(line, sizeof(line), fd) != NULL) {
61929964Sache      if ((cp = strchr(line, '\n')) != NULL)
62029964Sache        *cp = '\0';
62129964Sache      lreply(230, "%s", line);
62229964Sache    }
62329964Sache    (void) fflush(stdout);
62429964Sache    (void) fclose(fd);
62529964Sache  }
62629964Sache  }
62722347Spst#if DOANONYMOUS
62822347Spst  if (guest) {
62922347Spst    reply(230, "Guest login ok, access restrictions apply.");
63022347Spst#if DOTITLE
63139012Simp    snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%s", remotehost,
63239012Simp	passwd);
63322347Spst    setproctitle(proctitle);
63422347Spst#endif	/* DOTITLE */
63522347Spst    syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
63622347Spst            remotehost, passwd);
63722347Spst  } else
63822347Spst#endif	/* DOANONYMOUS */
63922347Spst  {
64022347Spst    reply(230, "User %s logged in.", pw->pw_name);
64122347Spst
64222347Spst#if DOTITLE
64339012Simp    snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
64422347Spst    setproctitle(proctitle);
64522347Spst#endif	/* DOTITLE */
64629964Sache    syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
64722347Spst  }
64822347Spst  home = pw->pw_dir;	/* home dir for globbing */
64922347Spst  umask(defumask);
65022347Spst  return;
65122347Spst
65222347Spstbad:
65322347Spst  /* Forget all about it... */
65422347Spst  end_login();
65522347Spst}
65622347Spst
65722347SpstVOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
65822347Spst{
65922347Spst  FILE *fin, *dout;
66022347Spst  struct stat st;
66122347Spst  int (*closefunc) ();
66222347Spst
66322347Spst  if (cmd == 0) {
66422347Spst    fin = fopen(name, "r"), closefunc = fclose;
66522347Spst    st.st_size = 0;
66622347Spst  } else {
66722347Spst    char line[BUFSIZ];
66822347Spst
66939012Simp    snprintf(line, sizeof(line), cmd, name);
67039012Simp    name = line;
67122347Spst    fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
67222347Spst    st.st_size = -1;
67322347Spst#if HAVE_ST_BLKSIZE
67422347Spst    st.st_blksize = BUFSIZ;
67522347Spst#endif /* HAVE_ST_BLKSIZE */
67622347Spst  }
67722347Spst  if (fin == NULL) {
67822347Spst    if (errno != 0)
67922347Spst      perror_reply(550, name);
68022347Spst    return;
68122347Spst  }
68222347Spst  if (cmd == 0 &&
68322347Spst      (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
68422347Spst    reply(550, "%s: not a plain file.", name);
68522347Spst    goto done;
68622347Spst  }
68722347Spst  if (restart_point) {
68822347Spst    if (type == TYPE_A) {
68922347Spst      register int i, n, c;
69022347Spst
69122347Spst      n = restart_point;
69222347Spst      i = 0;
69322347Spst      while (i++ < n) {
69422347Spst	if ((c = getc(fin)) == EOF) {
69522347Spst	  perror_reply(550, name);
69622347Spst	  goto done;
69722347Spst	}
69822347Spst	if (c == '\n')
69922347Spst	  i++;
70022347Spst      }
70122347Spst    } else
70222347Spst      if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
70322347Spst	perror_reply(550, name);
70422347Spst	goto done;
70522347Spst      }
70622347Spst  }
70722347Spst  dout = dataconn(name, st.st_size, "w");
70822347Spst  if (dout == NULL)
70922347Spst    goto done;
71022347Spst#if HAVE_ST_BLKSIZE
71122347Spst  send_data(fin, dout, st.st_blksize);
71222347Spst#else /* HAVE_ST_BLKSIZE */
71322347Spst  send_data(fin, dout, BUFSIZ);
71422347Spst#endif /* HAVE_ST_BLKSIZE */
71522347Spst  fclose(dout);
71622347Spst  data = -1;
71722347Spst  pdata = -1;
71822347Spstdone:
71922347Spst  (*closefunc) (fin);
72022347Spst}
72122347Spst
72222347SpstVOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
72322347Spst{
72422347Spst  FILE *fout, *din;
72522347Spst  struct stat st;
72622347Spst  int (*closefunc) ();
72722347Spst
72822347Spst  if (unique && stat(name, &st) == 0 &&
72922347Spst      (name = gunique(name)) == NULL)
73022347Spst    return;
73122347Spst
73222347Spst  if (restart_point)
73322347Spst    mode = "r+w";
73422347Spst  fout = fopen(name, mode);
73522347Spst  closefunc = fclose;
73622347Spst  if (fout == NULL) {
73722347Spst    perror_reply(553, name);
73822347Spst    return;
73922347Spst  }
74022347Spst  if (restart_point) {
74122347Spst    if (type == TYPE_A) {
74222347Spst      register int i, n, c;
74322347Spst
74422347Spst      n = restart_point;
74522347Spst      i = 0;
74622347Spst      while (i++ < n) {
74722347Spst	if ((c = getc(fout)) == EOF) {
74822347Spst	  perror_reply(550, name);
74922347Spst	  goto done;
75022347Spst	}
75122347Spst	if (c == '\n')
75222347Spst	  i++;
75322347Spst      }
75422347Spst      /* We must do this seek to "current" position because we are changing
75522347Spst         from reading to writing. */
75622347Spst      if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
75722347Spst	perror_reply(550, name);
75822347Spst	goto done;
75922347Spst      }
76022347Spst    } else
76122347Spst      if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
76222347Spst	perror_reply(550, name);
76322347Spst	goto done;
76422347Spst      }
76522347Spst  }
76622347Spst  din = dataconn(name, (off_t) - 1, "r");
76722347Spst  if (din == NULL)
76822347Spst    goto done;
76922347Spst  if (receive_data(din, fout) == 0) {
77022347Spst    if (unique)
77122347Spst      reply(226, "Transfer complete (unique file name:%s).",
77222347Spst	    name);
77322347Spst    else
77422347Spst      reply(226, "Transfer complete.");
77522347Spst  }
77622347Spst  fclose(din);
77722347Spst  data = -1;
77822347Spst  pdata = -1;
77922347Spstdone:
78022347Spst  (*closefunc) (fout);
78122347Spst}
78222347Spst
78322347Spststatic FILE *getdatasock FUNCTION((mode), char *mode)
78422347Spst{
78522347Spst  int s, on = 1, tries;
78622347Spst
78722347Spst  if (data >= 0)
78822347Spst    return (fdopen(data, mode));
78929964Sache  disable_signalling();
79022347Spst  if (seteuid((uid_t) 0))
79122347Spst    syslog(LOG_ERR, "Can't set euid");
79222347Spst  s = socket(AF_INET, SOCK_STREAM, 0);
79322347Spst  if (s < 0)
79422347Spst    goto bad;
79522347Spst  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
79622347Spst		 (char *) &on, sizeof(on)) < 0)
79722347Spst    goto bad;
79822347Spst  /* anchor socket to avoid multi-homing problems */
79922347Spst  data_source.sin_family = AF_INET;
80022347Spst  data_source.sin_addr = ctrl_addr.sin_addr;
80122347Spst  for (tries = 1;; tries++) {
80222347Spst    if (bind(s, (struct sockaddr *) & data_source,
80322347Spst	     sizeof(data_source)) >= 0)
80422347Spst      break;
80522347Spst    if (errno != EADDRINUSE || tries > 10)
80622347Spst      goto bad;
80722347Spst    sleep(tries);
80822347Spst  }
80922347Spst  if (seteuid((uid_t) pw->pw_uid))
81022347Spst    syslog(LOG_ERR, "Can't set euid");
81129964Sache  enable_signalling();
81222347Spst#ifdef IP_TOS
81322347Spst  on = IPTOS_THROUGHPUT;
81422347Spst  if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
81522347Spst    syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
81622347Spst#endif
81722347Spst  return (fdopen(s, mode));
81822347Spstbad:
81929964Sache  {
82029964Sache  int t = errno;
82129964Sache
82222347Spst  if (seteuid((uid_t) pw->pw_uid))
82322347Spst    syslog(LOG_ERR, "Can't set euid");
82429964Sache  enable_signalling();
82522347Spst  close(s);
82629964Sache
82729964Sache  errno = t;
82829964Sache  }
82922347Spst  return (NULL);
83022347Spst}
83122347Spst
83222347Spststatic FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
83322347Spst{
83422347Spst  char sizebuf[32];
83522347Spst  FILE *file;
83622347Spst  int retry = 0;
83722347Spst#ifdef IP_TOS
83822347Spst  int tos;
83922347Spst#endif /* IP_TOS */
84022347Spst
84122347Spst  file_size = size;
84222347Spst  byte_count = 0;
84322347Spst  if (size != (off_t) - 1)
84439012Simp    snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
84522347Spst  else
84622347Spst    strcpy(sizebuf, "");
84722347Spst  if (pdata >= 0) {
84822347Spst    struct sockaddr_in from;
84922347Spst    int s, fromlen = sizeof(from);
85022347Spst
85122347Spst    s = accept(pdata, (struct sockaddr *) & from, &fromlen);
85222347Spst    if (s < 0) {
85322347Spst      reply(425, "Can't open data connection.");
85422347Spst      close(pdata);
85522347Spst      pdata = -1;
85622347Spst      return (NULL);
85722347Spst    }
85822347Spst    close(pdata);
85922347Spst    pdata = s;
86022347Spst#ifdef IP_TOS
86122347Spst    tos = IPTOS_LOWDELAY;
86222347Spst    setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
86322347Spst		      sizeof(int));
86422347Spst
86522347Spst#endif
86622347Spst    reply(150, "Opening %s mode data connection for %s%s.",
86722347Spst	  type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
86822347Spst    return (fdopen(pdata, mode));
86922347Spst  }
87022347Spst  if (data >= 0) {
87122347Spst    reply(125, "Using existing data connection for %s%s.",
87222347Spst	  name, sizebuf);
87322347Spst    usedefault = 1;
87422347Spst    return (fdopen(data, mode));
87522347Spst  }
87622347Spst  if (usedefault)
87722347Spst    data_dest = his_addr;
87822347Spst  usedefault = 1;
87922347Spst  file = getdatasock(mode);
88022347Spst  if (file == NULL) {
88122347Spst    reply(425, "Can't create data socket (%s,%d): %s.",
88222347Spst	  inet_ntoa(data_source.sin_addr),
88322347Spst	  ntohs(data_source.sin_port), strerror(errno));
88422347Spst    return (NULL);
88522347Spst  }
88622347Spst  data = fileno(file);
88722347Spst  while (connect(data, (struct sockaddr *) & data_dest,
88822347Spst		 sizeof(data_dest)) < 0) {
88922347Spst    if (errno == EADDRINUSE && retry < swaitmax) {
89022347Spst      sleep((unsigned) swaitint);
89122347Spst      retry += swaitint;
89222347Spst      continue;
89322347Spst    }
89422347Spst    perror_reply(425, "Can't build data connection");
89522347Spst    fclose(file);
89622347Spst    data = -1;
89722347Spst    return (NULL);
89822347Spst  }
89922347Spst  reply(150, "Opening %s mode data connection for %s%s.",
90022347Spst	type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
90122347Spst  return (file);
90222347Spst}
90322347Spst
90422347Spst/*
90522347Spst * Tranfer the contents of "instr" to
90622347Spst * "outstr" peer using the appropriate
90722347Spst * encapsulation of the data subject
90822347Spst * to Mode, Structure, and Type.
90922347Spst *
91022347Spst * NB: Form isn't handled.
91122347Spst */
91222347Spststatic VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
91322347Spst{
91422347Spst  register int c, cnt;
91522347Spst  register char *buf;
91622347Spst  int netfd, filefd;
91722347Spst
91822347Spst  transflag++;
91922347Spst  if (setjmp(urgcatch)) {
92022347Spst    transflag = 0;
92122347Spst    return;
92222347Spst  }
92322347Spst  switch (type) {
92422347Spst
92522347Spst  case TYPE_A:
92622347Spst    while ((c = getc(instr)) != EOF) {
92722347Spst      byte_count++;
92822347Spst      if (c == '\n') {
92922347Spst	if (ferror(outstr))
93022347Spst	  goto data_err;
93122347Spst	putc('\r', outstr);
93222347Spst      }
93322347Spst      putc(c, outstr);
93422347Spst    }
93522347Spst    fflush(outstr);
93622347Spst    transflag = 0;
93722347Spst    if (ferror(instr))
93822347Spst      goto file_err;
93922347Spst    if (ferror(outstr))
94022347Spst      goto data_err;
94122347Spst    reply(226, "Transfer complete.");
94222347Spst    return;
94322347Spst
94422347Spst  case TYPE_I:
94522347Spst  case TYPE_L:
94622347Spst    if ((buf = malloc((u_int) blksize)) == NULL) {
94722347Spst      transflag = 0;
94822347Spst      perror_reply(451, "Local resource failure: malloc");
94922347Spst      return;
95022347Spst    }
95122347Spst    netfd = fileno(outstr);
95222347Spst    filefd = fileno(instr);
95322347Spst    while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
95422347Spst	   write(netfd, buf, cnt) == cnt)
95522347Spst      byte_count += cnt;
95622347Spst    transflag = 0;
95722347Spst    free(buf);
95822347Spst    if (cnt != 0) {
95922347Spst      if (cnt < 0)
96022347Spst	goto file_err;
96122347Spst      goto data_err;
96222347Spst    }
96322347Spst    reply(226, "Transfer complete.");
96422347Spst    return;
96522347Spst  default:
96622347Spst    transflag = 0;
96722347Spst    reply(550, "Unimplemented TYPE %d in send_data", type);
96822347Spst    return;
96922347Spst  }
97022347Spst
97122347Spstdata_err:
97222347Spst  transflag = 0;
97322347Spst  perror_reply(426, "Data connection");
97422347Spst  return;
97522347Spst
97622347Spstfile_err:
97722347Spst  transflag = 0;
97822347Spst  perror_reply(551, "Error on input file");
97922347Spst}
98022347Spst
98122347Spst/*
98222347Spst * Transfer data from peer to
98322347Spst * "outstr" using the appropriate
98422347Spst * encapulation of the data subject
98522347Spst * to Mode, Structure, and Type.
98622347Spst *
98722347Spst * N.B.: Form isn't handled.
98822347Spst */
98922347Spststatic int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
99022347Spst{
99122347Spst  register int c;
99222347Spst  int cnt, bare_lfs = 0;
99322347Spst  char buf[BUFSIZ];
99422347Spst
99522347Spst  transflag++;
99622347Spst  if (setjmp(urgcatch)) {
99722347Spst    transflag = 0;
99822347Spst    return (-1);
99922347Spst  }
100022347Spst  switch (type) {
100122347Spst
100222347Spst  case TYPE_I:
100322347Spst  case TYPE_L:
100422347Spst    while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
100522347Spst      if (write(fileno(outstr), buf, cnt) != cnt)
100622347Spst	goto file_err;
100722347Spst      byte_count += cnt;
100822347Spst    }
100922347Spst    if (cnt < 0)
101022347Spst      goto data_err;
101122347Spst    transflag = 0;
101222347Spst    return (0);
101322347Spst
101422347Spst  case TYPE_E:
101522347Spst    reply(553, "TYPE E not implemented.");
101622347Spst    transflag = 0;
101722347Spst    return (-1);
101822347Spst
101922347Spst  case TYPE_A:
102022347Spst    while ((c = getc(instr)) != EOF) {
102122347Spst      byte_count++;
102222347Spst      if (c == '\n')
102322347Spst	bare_lfs++;
102422347Spst      while (c == '\r') {
102522347Spst	if (ferror(outstr))
102622347Spst	  goto data_err;
102722347Spst	if ((c = getc(instr)) != '\n') {
102822347Spst	  putc('\r', outstr);
102922347Spst	  if (c == '\0' || c == EOF)
103022347Spst	    goto contin2;
103122347Spst	}
103222347Spst      }
103322347Spst      putc(c, outstr);
103422347Spst  contin2:;
103522347Spst    }
103622347Spst    fflush(outstr);
103722347Spst    if (ferror(instr))
103822347Spst      goto data_err;
103922347Spst    if (ferror(outstr))
104022347Spst      goto file_err;
104122347Spst    transflag = 0;
104222347Spst    if (bare_lfs) {
104322347Spst      lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
104422347Spst      printf("   File may not have transferred correctly.\r\n");
104522347Spst    }
104622347Spst    return (0);
104722347Spst  default:
104822347Spst    reply(550, "Unimplemented TYPE %d in receive_data", type);
104922347Spst    transflag = 0;
105022347Spst    return (-1);
105122347Spst  }
105222347Spst
105322347Spstdata_err:
105422347Spst  transflag = 0;
105522347Spst  perror_reply(426, "Data Connection");
105622347Spst  return (-1);
105722347Spst
105822347Spstfile_err:
105922347Spst  transflag = 0;
106022347Spst  perror_reply(452, "Error writing file");
106122347Spst  return (-1);
106222347Spst}
106322347Spst
106422347SpstVOIDRET statfilecmd FUNCTION((filename), char *filename)
106522347Spst{
106622347Spst  char line[BUFSIZ];
106722347Spst  FILE *fin;
106822347Spst  int c;
106922347Spst
107022347Spst#if HAVE_LS_G_FLAG
107139012Simp  snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
107222347Spst#else /* HAVE_LS_G_FLAG */
107339012Simp  snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
107422347Spst#endif /* HAVE_LS_G_FLAG */
107522347Spst  fin = ftpd_popen(line, "r");
107622347Spst  lreply(211, "status of %s:", filename);
107722347Spst  while ((c = getc(fin)) != EOF) {
107822347Spst    if (c == '\n') {
107922347Spst      if (ferror(stdout)) {
108022347Spst	perror_reply(421, "control connection");
108122347Spst	ftpd_pclose(fin);
108222347Spst	dologout(1);
108322347Spst	/* NOTREACHED */
108422347Spst      }
108522347Spst      if (ferror(fin)) {
108622347Spst	perror_reply(551, filename);
108722347Spst	ftpd_pclose(fin);
108822347Spst	return;
108922347Spst      }
109022347Spst      putc('\r', stdout);
109122347Spst    }
109222347Spst    putc(c, stdout);
109322347Spst  }
109422347Spst  ftpd_pclose(fin);
109522347Spst  reply(211, "End of Status");
109622347Spst}
109722347Spst
109822347SpstVOIDRET statcmd FUNCTION_NOARGS
109922347Spst{
110022347Spst/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
110122347Spst  struct sockaddr_in *sin;
110222347Spst  u_char *a, *p;
110322347Spst
110422347Spst  lreply(211, "FTP server status:");
110522347Spst  printf("     \r\n");
110622347Spst  printf("     Connected to %s", remotehost);
110722347Spst  if (!isdigit(remotehost[0]))
110822347Spst    printf(" (%s)", inet_ntoa(his_addr.sin_addr));
110922347Spst  printf("\r\n");
111022347Spst  if (logged_in) {
111122347Spst#if DOANONYMOUS
111222347Spst    if (guest)
111322347Spst      printf("     Logged in anonymously\r\n");
111422347Spst    else
111522347Spst#endif	/* DOANONYMOUS */
111622347Spst      printf("     Logged in as %s\r\n", pw->pw_name);
111722347Spst  } else
111822347Spst    if (askpasswd)
111922347Spst      printf("     Waiting for password\r\n");
112022347Spst    else
112122347Spst      printf("     Waiting for user name\r\n");
112222347Spst  if (data != -1)
112322347Spst    printf("     Data connection open\r\n");
112422347Spst  else
112522347Spst    if (pdata != -1) {
112622347Spst      printf("     in Passive mode");
112722347Spst      sin = &pasv_addr;
112822347Spst      goto printaddr;
112922347Spst    } else
113022347Spst      if (usedefault == 0) {
113122347Spst	printf("     PORT");
113222347Spst	sin = &data_dest;
113322347Spst    printaddr:
113422347Spst	a = (u_char *) & sin->sin_addr;
113522347Spst	p = (u_char *) & sin->sin_port;
113622347Spst#define UC(b) (((int) b) & 0xff)
113722347Spst	printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
113822347Spst	       UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
113922347Spst#undef UC
114022347Spst      } else
114122347Spst	printf("     No data connection\r\n");
114222347Spst  reply(211, "End of status");
114322347Spst}
114422347Spst
114522347SpstVOIDRET opiefatal FUNCTION((s), char *s)
114622347Spst{
114722347Spst  reply(451, "Error in server: %s\n", s);
114822347Spst  reply(221, "Closing connection due to server error.");
114922347Spst  dologout(0);
115022347Spst  /* NOTREACHED */
115122347Spst}
115222347Spst
115322347Spststatic VOIDRET ack FUNCTION((s), char *s)
115422347Spst{
115522347Spst  reply(250, "%s command successful.", s);
115622347Spst}
115722347Spst
115822347SpstVOIDRET nack FUNCTION((s), char *s)
115922347Spst{
116022347Spst  reply(502, "%s command not implemented.", s);
116122347Spst}
116222347Spst
116322347SpstVOIDRET yyerror FUNCTION((s), char *s)
116422347Spst{
116522347Spst  char *cp;
116622347Spst
116722347Spst  if (cp = strchr(cbuf, '\n'))
116822347Spst    *cp = '\0';
116922347Spst  reply(500, "'%s': command not understood.", cbuf);
117022347Spst}
117122347Spst
117222347SpstVOIDRET delete FUNCTION((name), char *name)
117322347Spst{
117422347Spst  struct stat st;
117522347Spst
117622347Spst  if (stat(name, &st) < 0) {
117722347Spst    perror_reply(550, name);
117822347Spst    return;
117922347Spst  }
118022347Spst  if ((st.st_mode & S_IFMT) == S_IFDIR) {
118122347Spst    if (rmdir(name) < 0) {
118222347Spst      perror_reply(550, name);
118322347Spst      return;
118422347Spst    }
118522347Spst    goto done;
118622347Spst  }
118722347Spst  if (unlink(name) < 0) {
118822347Spst    perror_reply(550, name);
118922347Spst    return;
119022347Spst  }
119122347Spstdone:
119222347Spst  ack("DELE");
119322347Spst}
119422347Spst
119522347SpstVOIDRET cwd FUNCTION((path), char *path)
119622347Spst{
119722347Spst  if (chdir(path) < 0)
119822347Spst    perror_reply(550, path);
119922347Spst  else
120022347Spst    ack("CWD");
120122347Spst}
120222347Spst
120322347SpstVOIDRET makedir FUNCTION((name), char *name)
120422347Spst{
120522347Spst  if (mkdir(name, 0777) < 0)
120622347Spst    perror_reply(550, name);
120722347Spst  else
120822347Spst    reply(257, "MKD command successful.");
120922347Spst}
121022347Spst
121122347SpstVOIDRET removedir FUNCTION((name), char *name)
121222347Spst{
121322347Spst  if (rmdir(name) < 0)
121422347Spst    perror_reply(550, name);
121522347Spst  else
121622347Spst    ack("RMD");
121722347Spst}
121822347Spst
121922347SpstVOIDRET pwd FUNCTION_NOARGS
122022347Spst{
122122347Spst  char path[MAXPATHLEN + 1];
122222347Spst
122322347Spst  if (getcwd(path, MAXPATHLEN) == (char *) NULL)
122422347Spst    reply(550, "%s.", path);
122522347Spst  else
122622347Spst    reply(257, "\"%s\" is current directory.", path);
122722347Spst}
122822347Spst
122922347Spstchar *renamefrom FUNCTION((name), char *name)
123022347Spst{
123122347Spst  struct stat st;
123222347Spst
123322347Spst  if (stat(name, &st) < 0) {
123422347Spst    perror_reply(550, name);
123522347Spst    return ((char *) 0);
123622347Spst  }
123722347Spst  reply(350, "File exists, ready for destination name");
123822347Spst  return (name);
123922347Spst}
124022347Spst
124122347SpstVOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
124222347Spst{
124322347Spst  if (rename(from, to) < 0)
124422347Spst    perror_reply(550, "rename");
124522347Spst  else
124622347Spst    ack("RNTO");
124722347Spst}
124822347Spst
124922347Spststatic VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
125022347Spst{
125122347Spst  struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
125222347Spst				     sizeof(struct in_addr), AF_INET);
125322347Spst  time_t t, time();
125422347Spst
125522347Spst  if (hp)
125622347Spst    strncpy(remotehost, hp->h_name, sizeof(remotehost));
125722347Spst  else
125822347Spst    strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
125939012Simp  remotehost[sizeof(remotehost) - 1] = '\0';
126022347Spst#if DOTITLE
126139012Simp  snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
126222347Spst  setproctitle(proctitle);
126322347Spst#endif	/* DOTITLE */
126422347Spst
126522347Spst  t = time((time_t *) 0);
126622347Spst  syslog(LOG_INFO, "connection from %s at %s",
126722347Spst    remotehost, ctime(&t));
126822347Spst}
126922347Spst
127022347Spst/*
127122347Spst * Record logout in wtmp file
127222347Spst * and exit with supplied status.
127322347Spst */
127422347SpstVOIDRET dologout FUNCTION((status), int status)
127522347Spst{
127629964Sache  disable_signalling();
127722347Spst  if (logged_in) {
127822347Spst    if (seteuid((uid_t) 0))
127922347Spst      syslog(LOG_ERR, "Can't set euid");
128029964Sache    opielogwtmp(ttyline, "", "");
128122347Spst  }
128222347Spst  /* beware of flushing buffers after a SIGPIPE */
128322347Spst  _exit(status);
128422347Spst}
128522347Spst
128622347Spststatic VOIDRET myoob FUNCTION((input), int input)
128722347Spst{
128822347Spst  char *cp;
128922347Spst
129022347Spst  /* only process if transfer occurring */
129122347Spst  if (!transflag)
129222347Spst    return;
129322347Spst  cp = tmpline;
129422347Spst  if (getline(cp, 7, stdin) == NULL) {
129522347Spst    reply(221, "You could at least say goodbye.");
129622347Spst    dologout(0);
129722347Spst  }
129822347Spst  upper(cp);
129922347Spst  if (strcmp(cp, "ABOR\r\n") == 0) {
130022347Spst    tmpline[0] = '\0';
130122347Spst    reply(426, "Transfer aborted. Data connection closed.");
130222347Spst    reply(226, "Abort successful");
130322347Spst    longjmp(urgcatch, 1);
130422347Spst  }
130522347Spst  if (strcmp(cp, "STAT\r\n") == 0) {
130622347Spst    if (file_size != (off_t) - 1)
130722347Spst      reply(213, "Status: %lu of %lu bytes transferred",
130822347Spst	    byte_count, file_size);
130922347Spst    else
131022347Spst      reply(213, "Status: %lu bytes transferred", byte_count);
131122347Spst  }
131222347Spst}
131322347Spst
131422347Spst/*
131522347Spst * Note: a response of 425 is not mentioned as a possible response to
131622347Spst *      the PASV command in RFC959. However, it has been blessed as
131722347Spst *      a legitimate response by Jon Postel in a telephone conversation
131822347Spst *      with Rick Adams on 25 Jan 89.
131922347Spst */
132022347SpstVOIDRET passive FUNCTION_NOARGS
132122347Spst{
132222347Spst  int len;
132322347Spst  register char *p, *a;
132422347Spst
132522347Spst  pdata = socket(AF_INET, SOCK_STREAM, 0);
132622347Spst  if (pdata < 0) {
132722347Spst    perror_reply(425, "Can't open passive connection");
132822347Spst    return;
132922347Spst  }
133022347Spst  pasv_addr = ctrl_addr;
133122347Spst  pasv_addr.sin_port = 0;
133222347Spst  if (seteuid((uid_t) 0))
133322347Spst    syslog(LOG_ERR, "Can't set euid");
133422347Spst  if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
133522347Spst    seteuid((uid_t) pw->pw_uid);
133622347Spst    goto pasv_error;
133722347Spst  }
133822347Spst  if (seteuid((uid_t) pw->pw_uid))
133922347Spst    syslog(LOG_ERR, "Can't set euid");
134022347Spst  len = sizeof(pasv_addr);
134122347Spst  if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
134222347Spst    goto pasv_error;
134322347Spst  if (listen(pdata, 1) < 0)
134422347Spst    goto pasv_error;
134522347Spst  a = (char *) &pasv_addr.sin_addr;
134622347Spst  p = (char *) &pasv_addr.sin_port;
134722347Spst
134822347Spst#define UC(b) (((int) b) & 0xff)
134922347Spst
135022347Spst  reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
135122347Spst	UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
135222347Spst  return;
135322347Spst
135422347Spstpasv_error:
135522347Spst  close(pdata);
135622347Spst  pdata = -1;
135722347Spst  perror_reply(425, "Can't open passive connection");
135822347Spst  return;
135922347Spst}
136022347Spst
136122347Spst/*
136222347Spst * Generate unique name for file with basename "local".
136322347Spst * The file named "local" is already known to exist.
136422347Spst * Generates failure reply on error.
136522347Spst */
136622347Spststatic char *gunique FUNCTION((local), char *local)
136722347Spst{
136829964Sache  static char new[MAXPATHLEN+1];
136922347Spst  struct stat st;
137022347Spst  char *cp = strrchr(local, '/');
137122347Spst  int count = 0;
137222347Spst
137322347Spst  if (cp)
137422347Spst    *cp = '\0';
137522347Spst  if (stat(cp ? local : ".", &st) < 0) {
137622347Spst    perror_reply(553, cp ? local : ".");
137722347Spst    return ((char *) 0);
137822347Spst  }
137922347Spst  if (cp)
138022347Spst    *cp = '/';
138122347Spst  strcpy(new, local);
138222347Spst  cp = new + strlen(new);
138322347Spst  *cp++ = '.';
138422347Spst  for (count = 1; count < 100; count++) {
138539012Simp    snprintf(cp, sizeof(new) - (cp - new), "%d", count);
138622347Spst    if (stat(new, &st) < 0)
138722347Spst      return (new);
138822347Spst  }
138922347Spst  reply(452, "Unique file name cannot be created.");
139022347Spst  return ((char *) 0);
139122347Spst}
139222347Spst
139322347Spst/*
139422347Spst * Format and send reply containing system error number.
139522347Spst */
139622347SpstVOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
139722347Spst{
139822347Spst  reply(code, "%s: %s.", string, strerror(errno));
139922347Spst}
140022347Spst
140122347Spststatic char *onefile[] =
140222347Spst{
140322347Spst  "",
140422347Spst  0
140522347Spst};
140622347Spst
140722347SpstVOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
140822347Spst{
140922347Spst  struct stat st;
141022347Spst  DIR *dirp = NULL;
141122347Spst  struct dirent *dir;
141222347Spst  FILE *dout = NULL;
141322347Spst  register char **dirlist, *dirname;
141422347Spst  int simple = 0;
141522347Spst
141622347Spst  if (strpbrk(whichfiles, "~{[*?") != NULL) {
141722347Spst    extern char **ftpglob(), *globerr;
141822347Spst
141922347Spst    globerr = NULL;
142022347Spst    dirlist = ftpglob(whichfiles);
142122347Spst    if (globerr != NULL) {
142222347Spst      reply(550, globerr);
142322347Spst      return;
142422347Spst    } else
142522347Spst      if (dirlist == NULL) {
142622347Spst	errno = ENOENT;
142722347Spst	perror_reply(550, whichfiles);
142822347Spst	return;
142922347Spst      }
143022347Spst  } else {
143122347Spst    onefile[0] = whichfiles;
143222347Spst    dirlist = onefile;
143322347Spst    simple = 1;
143422347Spst  }
143522347Spst
143622347Spst  if (setjmp(urgcatch)) {
143722347Spst    transflag = 0;
143822347Spst    return;
143922347Spst  }
144022347Spst  while (dirname = *dirlist++) {
144122347Spst    if (stat(dirname, &st) < 0) {
144222347Spst      /* If user typed "ls -l", etc, and the client used NLST, do what the
144322347Spst         user meant. */
144422347Spst      if (dirname[0] == '-' && *dirlist == NULL &&
144522347Spst	  transflag == 0) {
144622347Spst	retrieve("/bin/ls %s", dirname);
144722347Spst	return;
144822347Spst      }
144922347Spst      perror_reply(550, whichfiles);
145022347Spst      if (dout != NULL) {
145122347Spst	fclose(dout);
145222347Spst	transflag = 0;
145322347Spst	data = -1;
145422347Spst	pdata = -1;
145522347Spst      }
145622347Spst      return;
145722347Spst    }
145822347Spst    if ((st.st_mode & S_IFMT) == S_IFREG) {
145922347Spst      if (dout == NULL) {
146022347Spst	dout = dataconn("file list", (off_t) - 1, "w");
146122347Spst	if (dout == NULL)
146222347Spst	  return;
146322347Spst	transflag++;
146422347Spst      }
146522347Spst      fprintf(dout, "%s%s\n", dirname,
146622347Spst	      type == TYPE_A ? "\r" : "");
146722347Spst      byte_count += strlen(dirname) + 1;
146822347Spst      continue;
146922347Spst    } else
147022347Spst      if ((st.st_mode & S_IFMT) != S_IFDIR)
147122347Spst	continue;
147222347Spst
147322347Spst    if ((dirp = opendir(dirname)) == NULL)
147422347Spst      continue;
147522347Spst
147622347Spst    while ((dir = readdir(dirp)) != NULL) {
147729964Sache      char nbuf[MAXPATHLEN+1];
147822347Spst
147922347Spst      if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
148022347Spst	continue;
148122347Spst      if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
148222347Spst	  (strlen(dir->d_name) == 2))
148322347Spst	continue;
148422347Spst
148539012Simp      snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
148622347Spst
148722347Spst      /* We have to do a stat to insure it's not a directory or special file. */
148822347Spst      if (simple || (stat(nbuf, &st) == 0 &&
148922347Spst		     (st.st_mode & S_IFMT) == S_IFREG)) {
149022347Spst	if (dout == NULL) {
149122347Spst	  dout = dataconn("file list", (off_t) - 1, "w");
149222347Spst	  if (dout == NULL)
149322347Spst	    return;
149422347Spst	  transflag++;
149522347Spst	}
149622347Spst	if (nbuf[0] == '.' && nbuf[1] == '/')
149722347Spst	  fprintf(dout, "%s%s\n", &nbuf[2],
149822347Spst		  type == TYPE_A ? "\r" : "");
149922347Spst	else
150022347Spst	  fprintf(dout, "%s%s\n", nbuf,
150122347Spst		  type == TYPE_A ? "\r" : "");
150222347Spst	byte_count += strlen(nbuf) + 1;
150322347Spst      }
150422347Spst    }
150522347Spst    closedir(dirp);
150622347Spst  }
150722347Spst
150822347Spst  if (dout == NULL)
150922347Spst    reply(550, "No files found.");
151022347Spst  else
151122347Spst    if (ferror(dout) != 0)
151222347Spst      perror_reply(550, "Data connection");
151322347Spst    else
151422347Spst      reply(226, "Transfer complete.");
151522347Spst
151622347Spst  transflag = 0;
151722347Spst  if (dout != NULL)
151822347Spst    fclose(dout);
151922347Spst  data = -1;
152022347Spst  pdata = -1;
152122347Spst}
152222347Spst
152322347Spst#if DOTITLE
152422347Spst/*
152522347Spst * clobber argv so ps will show what we're doing.
152622347Spst * (stolen from sendmail)
152722347Spst * warning, since this is usually started from inetd.conf, it
152822347Spst * often doesn't have much of an environment or arglist to overwrite.
152922347Spst */
153022347SpstVOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
153122347Spst{
153222347Spst  register char *p, *bp, ch;
153322347Spst  register int i;
153422347Spst  char buf[BUFSIZ];
153522347Spst
153639012Simp  snprintf(buf, sizeof(buf), fmt, a, b, c);
153722347Spst
153822347Spst  /* make ps print our process name */
153922347Spst  p = Argv[0];
154022347Spst  *p++ = '-';
154122347Spst
154222347Spst  i = strlen(buf);
154322347Spst  if (i > LastArgv - p - 2) {
154422347Spst    i = LastArgv - p - 2;
154522347Spst    buf[i] = '\0';
154622347Spst  }
154722347Spst  bp = buf;
154822347Spst  while (ch = *bp++)
154922347Spst    if (ch != '\n' && ch != '\r')
155022347Spst      *p++ = ch;
155122347Spst  while (p < LastArgv)
155222347Spst    *p++ = ' ';
155322347Spst}
155422347Spst#endif	/* DOTITLE */
155522347Spst
155629964SacheVOIDRET catchexit FUNCTION_NOARGS
155722347Spst{
155822347Spst  closelog();
155922347Spst}
156022347Spst
156122347Spstint main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
156222347Spst{
156322347Spst  int addrlen, on = 1;
156422347Spst  char *cp;
156522347Spst#ifdef IP_TOS
156622347Spst  int tos;
156722347Spst#endif /* IP_TOS */
156822347Spst
156922347Spst  {
157022347Spst  int i;
157122347Spst
157222347Spst  for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
157322347Spst    close(i);
157422347Spst  }
157522347Spst
157622347Spst  /* LOG_NDELAY sets up the logging connection immediately, necessary for
157722347Spst     anonymous ftp's that chroot and can't do it later. */
157822347Spst  openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
157922347Spst  atexit(catchexit);
158022347Spst  addrlen = sizeof(his_addr);
158122347Spst  if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
158222347Spst    syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
158322347Spst    exit(1);
158422347Spst  }
158522347Spst  addrlen = sizeof(ctrl_addr);
158622347Spst  if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
158722347Spst    syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
158822347Spst    exit(1);
158922347Spst  }
159022347Spst#ifdef IP_TOS
159122347Spst  tos = IPTOS_LOWDELAY;
159222347Spst  if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
159322347Spst    syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
159422347Spst#endif
159522347Spst  data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
159622347Spst  debug = 0;
159722347Spst#if DOTITLE
159822347Spst  /* Save start and extent of argv for setproctitle. */
159922347Spst  Argv = argv;
160022347Spst  while (*envp)
160122347Spst    envp++;
160222347Spst  LastArgv = envp[-1] + strlen(envp[-1]);
160322347Spst#endif	/* DOTITLE */
160422347Spst
160522347Spst  argc--, argv++;
160622347Spst  while (argc > 0 && *argv[0] == '-') {
160722347Spst    for (cp = &argv[0][1]; *cp; cp++)
160822347Spst      switch (*cp) {
160922347Spst
161022347Spst      case 'v':
161122347Spst	debug = 1;
161222347Spst	break;
161322347Spst
161422347Spst      case 'd':
161522347Spst	debug = 1;
161622347Spst	break;
161722347Spst
161822347Spst      case 'l':
161922347Spst	break;
162022347Spst
162122347Spst      case 't':
162222347Spst	timeout = atoi(++cp);
162322347Spst	if (maxtimeout < timeout)
162422347Spst	  maxtimeout = timeout;
162522347Spst	goto nextopt;
162622347Spst
162722347Spst      case 'T':
162822347Spst	maxtimeout = atoi(++cp);
162922347Spst	if (timeout > maxtimeout)
163022347Spst	  timeout = maxtimeout;
163122347Spst	goto nextopt;
163222347Spst
163322347Spst      case 'u':
163422347Spst	{
163522347Spst	  int val = 0;
163622347Spst
163722347Spst	  while (*++cp && *cp >= '0' && *cp <= '9')
163822347Spst	    val = val * 8 + *cp - '0';
163922347Spst	  if (*cp)
164022347Spst	    fprintf(stderr, "ftpd: Bad value for -u\n");
164122347Spst	  else
164222347Spst	    defumask = val;
164322347Spst	  goto nextopt;
164422347Spst	}
164522347Spst
164622347Spst      default:
164722347Spst	fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
164822347Spst		*cp);
164922347Spst	break;
165022347Spst      }
165122347Spstnextopt:
165222347Spst    argc--, argv++;
165322347Spst  }
165422347Spst  freopen(_PATH_DEVNULL, "w", stderr);
165522347Spst  signal(SIGCHLD, SIG_IGN);
165629964Sache  enable_signalling();
165722347Spst
165822347Spst  /* Try to handle urgent data inline */
165922347Spst#ifdef SO_OOBINLINE
166022347Spst  if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
166122347Spst    syslog(LOG_ERR, "setsockopt: %m");
166222347Spst#endif
166322347Spst
166422347Spst#ifdef	F_SETOWN
166522347Spst  if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
166622347Spst    syslog(LOG_ERR, "fcntl F_SETOWN: %m");
166722347Spst#endif
166822347Spst  dolog(&his_addr);
166922347Spst  /* Set up default state */
167022347Spst  data = -1;
167122347Spst  type = TYPE_A;
167222347Spst  form = FORM_N;
167322347Spst  stru = STRU_F;
167422347Spst  mode = MODE_S;
167522347Spst  tmpline[0] = '\0';
167622347Spst  af_pwok = opieaccessfile(remotehost);
167722347Spst
167822347Spst  {
167929964Sache  FILE *fd;
168029964Sache  char line[128];
168122347Spst
168229964Sache  /* If logins are disabled, print out the message. */
168329964Sache  if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
168429964Sache    while (fgets(line, sizeof(line), fd) != NULL) {
168529964Sache      if ((cp = strchr(line, '\n')) != NULL)
168629964Sache        *cp = '\0';
168729964Sache      lreply(530, "%s", line);
168829964Sache    }
168929964Sache    (void) fflush(stdout);
169029964Sache    (void) fclose(fd);
169129964Sache    reply(530, "System not available.");
169229964Sache    exit(0);
169322347Spst  }
169429964Sache  if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
169529964Sache    while (fgets(line, sizeof(line), fd) != NULL) {
169629964Sache      if ((cp = strchr(line, '\n')) != NULL)
169729964Sache        *cp = '\0';
169829964Sache      lreply(220, "%s", line);
169929964Sache    }
170029964Sache    (void) fflush(stdout);
170129964Sache    (void) fclose(fd);
170229964Sache    /* reply(220,) must follow */
170329964Sache  }
170429964Sache  };
170522347Spst
170622347Spst  reply(220, "FTP server ready.");
170722347Spst
170822347Spst  setjmp(errcatch);
170922347Spst  for (;;)
171022347Spst    yyparse();
171122347Spst  /* NOTREACHED */
171222347Spst  return 0;
171322347Spst}
1714