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