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