122499Sdist /*
266711Spendry * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
361427Sbostic * The Regents of the University of California. All rights reserved.
433738Sbostic *
542666Sbostic * %sccs.include.redist.c%
622499Sdist */
722499Sdist
810275Ssam #ifndef lint
961427Sbostic static char copyright[] =
1066711Spendry "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
1161427Sbostic The Regents of the University of California. All rights reserved.\n";
1233738Sbostic #endif /* not lint */
1310275Ssam
1422499Sdist #ifndef lint
15*68976Sbostic static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 04/28/95";
1633738Sbostic #endif /* not lint */
1722499Sdist
1810275Ssam /*
1910275Ssam * FTP server.
2010275Ssam */
2110303Ssam #include <sys/param.h>
2210275Ssam #include <sys/stat.h>
2310275Ssam #include <sys/ioctl.h>
2410275Ssam #include <sys/socket.h>
2513595Ssam #include <sys/wait.h>
2610275Ssam
2710275Ssam #include <netinet/in.h>
2844339Skarels #include <netinet/in_systm.h>
2944339Skarels #include <netinet/ip.h>
3010275Ssam
3136933Skarels #define FTP_NAMES
3213034Ssam #include <arpa/ftp.h>
3313211Sroot #include <arpa/inet.h>
3426044Sminshall #include <arpa/telnet.h>
3513034Ssam
3666711Spendry #include <ctype.h>
3746669Sbostic #include <dirent.h>
3866711Spendry #include <err.h>
3966711Spendry #include <errno.h>
4046669Sbostic #include <fcntl.h>
4166711Spendry #include <glob.h>
4266711Spendry #include <limits.h>
4366711Spendry #include <netdb.h>
4410275Ssam #include <pwd.h>
4510275Ssam #include <setjmp.h>
4666711Spendry #include <signal.h>
4746669Sbostic #include <stdio.h>
4846669Sbostic #include <stdlib.h>
4946669Sbostic #include <string.h>
5066711Spendry #include <syslog.h>
5166711Spendry #include <time.h>
5266711Spendry #include <unistd.h>
5366711Spendry
5437459Skarels #include "pathnames.h"
5555258Sandrew #include "extern.h"
5610275Ssam
5755258Sandrew #if __STDC__
5855258Sandrew #include <stdarg.h>
5955258Sandrew #else
6055258Sandrew #include <varargs.h>
6155258Sandrew #endif
6255258Sandrew
6366711Spendry static char version[] = "Version 6.00";
6466711Spendry
6555258Sandrew extern off_t restart_point;
6626044Sminshall extern char cbuf[];
6710275Ssam
6810275Ssam struct sockaddr_in ctrl_addr;
6910275Ssam struct sockaddr_in data_source;
7010275Ssam struct sockaddr_in data_dest;
7110275Ssam struct sockaddr_in his_addr;
7236933Skarels struct sockaddr_in pasv_addr;
7310275Ssam
7410275Ssam int data;
7526044Sminshall jmp_buf errcatch, urgcatch;
7610275Ssam int logged_in;
7710275Ssam struct passwd *pw;
7810275Ssam int debug;
7926493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */
8036933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
8111757Ssam int logging;
8210275Ssam int guest;
8310275Ssam int type;
8410275Ssam int form;
8510275Ssam int stru; /* avoid C keyword */
8610275Ssam int mode;
8710321Ssam int usedefault = 1; /* for data transfers */
8836304Skarels int pdata = -1; /* for passive mode */
8966711Spendry sig_atomic_t transflag;
9036933Skarels off_t file_size;
9136933Skarels off_t byte_count;
9236933Skarels #if !defined(CMASK) || CMASK == 0
9336933Skarels #undef CMASK
9436933Skarels #define CMASK 027
9536933Skarels #endif
9636933Skarels int defumask = CMASK; /* default umask value */
9726044Sminshall char tmpline[7];
9836276Sbostic char hostname[MAXHOSTNAMELEN];
9936276Sbostic char remotehost[MAXHOSTNAMELEN];
10010275Ssam
10111653Ssam /*
10211653Ssam * Timeout intervals for retrying connections
10311653Ssam * to hosts that don't accept PORT cmds. This
10411653Ssam * is a kludge, but given the problems with TCP...
10511653Ssam */
10611653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */
10711653Ssam #define SWAITINT 5 /* interval between retries */
10811653Ssam
10911653Ssam int swaitmax = SWAITMAX;
11011653Ssam int swaitint = SWAITINT;
11111653Ssam
11236620Srick #ifdef SETPROCTITLE
11336620Srick char **Argv = NULL; /* pointer to argument vector */
11436620Srick char *LastArgv = NULL; /* end of argv */
11566711Spendry char proctitle[LINE_MAX]; /* initial part of title */
11636620Srick #endif /* SETPROCTITLE */
11736620Srick
11855258Sandrew #define LOGCMD(cmd, file) \
11955258Sandrew if (logging > 1) \
12055258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \
12155258Sandrew *(file) == '/' ? "" : curdir(), file);
12255258Sandrew #define LOGCMD2(cmd, file1, file2) \
12355258Sandrew if (logging > 1) \
12455258Sandrew syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
12555258Sandrew *(file1) == '/' ? "" : curdir(), file1, \
12655258Sandrew *(file2) == '/' ? "" : curdir(), file2);
12755258Sandrew #define LOGBYTES(cmd, file, cnt) \
12855258Sandrew if (logging > 1) { \
12955258Sandrew if (cnt == (off_t)-1) \
13055258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \
13155258Sandrew *(file) == '/' ? "" : curdir(), file); \
13255258Sandrew else \
13355258Sandrew syslog(LOG_INFO, "%s %s%s = %qd bytes", \
13455258Sandrew cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
13555258Sandrew }
13655258Sandrew
13755258Sandrew static void ack __P((char *));
13855258Sandrew static void myoob __P((int));
13955258Sandrew static int checkuser __P((char *));
14055258Sandrew static FILE *dataconn __P((char *, off_t, char *));
14155258Sandrew static void dolog __P((struct sockaddr_in *));
14255258Sandrew static char *curdir __P((void));
14355258Sandrew static void end_login __P((void));
14455258Sandrew static FILE *getdatasock __P((char *));
14555258Sandrew static char *gunique __P((char *));
14655258Sandrew static void lostconn __P((int));
14755258Sandrew static int receive_data __P((FILE *, FILE *));
14855258Sandrew static void send_data __P((FILE *, FILE *, off_t));
14955258Sandrew static struct passwd *
15055258Sandrew sgetpwnam __P((char *));
15155258Sandrew static char *sgetsave __P((char *));
15255258Sandrew
15355258Sandrew static char *
curdir()15455258Sandrew curdir()
15555258Sandrew {
15655258Sandrew static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */
15755258Sandrew
15855258Sandrew if (getcwd(path, sizeof(path)-2) == NULL)
15955258Sandrew return ("");
16055258Sandrew if (path[1] != '\0') /* special case for root dir. */
16155258Sandrew strcat(path, "/");
16255258Sandrew /* For guest account, skip / since it's chrooted */
16355258Sandrew return (guest ? path+1 : path);
16455258Sandrew }
16555258Sandrew
16655258Sandrew int
main(argc,argv,envp)16736620Srick main(argc, argv, envp)
16810275Ssam int argc;
16910275Ssam char *argv[];
17036620Srick char **envp;
17110275Ssam {
17266711Spendry int addrlen, ch, on = 1, tos;
17366711Spendry char *cp, line[LINE_MAX];
17455258Sandrew FILE *fd;
17510275Ssam
17645028Skarels /*
17745028Skarels * LOG_NDELAY sets up the logging connection immediately,
17845028Skarels * necessary for anonymous ftp's that chroot and can't do it later.
17945028Skarels */
18055258Sandrew openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
18166711Spendry addrlen = sizeof(his_addr);
18236304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
18326493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
18410275Ssam exit(1);
18510275Ssam }
18666711Spendry addrlen = sizeof(ctrl_addr);
18736304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
18826493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
18916339Skarels exit(1);
19016339Skarels }
19144339Skarels #ifdef IP_TOS
19244339Skarels tos = IPTOS_LOWDELAY;
19344339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
19444339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
19544339Skarels #endif
19616339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
19710275Ssam debug = 0;
19836620Srick #ifdef SETPROCTITLE
19936620Srick /*
20036620Srick * Save start and extent of argv for setproctitle.
20136620Srick */
20236620Srick Argv = argv;
20336620Srick while (*envp)
20436620Srick envp++;
20536620Srick LastArgv = envp[-1] + strlen(envp[-1]);
20636620Srick #endif /* SETPROCTITLE */
20736620Srick
20866711Spendry while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) {
20966711Spendry switch (ch) {
21010275Ssam case 'd':
21110275Ssam debug = 1;
21210275Ssam break;
21310275Ssam
21411757Ssam case 'l':
21555258Sandrew logging++; /* > 1 == extra logging */
21611757Ssam break;
21711757Ssam
21811653Ssam case 't':
21966711Spendry timeout = atoi(optarg);
22036933Skarels if (maxtimeout < timeout)
22136933Skarels maxtimeout = timeout;
22266711Spendry break;
22311653Ssam
22436933Skarels case 'T':
22566711Spendry maxtimeout = atoi(optarg);
22636933Skarels if (timeout > maxtimeout)
22736933Skarels timeout = maxtimeout;
22866711Spendry break;
22936933Skarels
23036933Skarels case 'u':
23136933Skarels {
23266711Spendry long val = 0;
23336933Skarels
23466711Spendry val = strtol(optarg, &optarg, 8);
23566711Spendry if (*optarg != '\0' || val < 0)
23666711Spendry warnx("bad value for -u");
23736933Skarels else
23836933Skarels defumask = val;
23966711Spendry break;
24036933Skarels }
24136933Skarels
24266711Spendry case 'v':
24366711Spendry debug = 1;
24466711Spendry break;
24566711Spendry
24610275Ssam default:
24766711Spendry warnx("unknown flag -%c ignored", optopt);
24810275Ssam break;
24910275Ssam }
25010275Ssam }
25137459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr);
25226493Sminshall (void) signal(SIGPIPE, lostconn);
25326493Sminshall (void) signal(SIGCHLD, SIG_IGN);
25435691Sbostic if ((int)signal(SIGURG, myoob) < 0)
25526493Sminshall syslog(LOG_ERR, "signal: %m");
25635691Sbostic
25738134Srick /* Try to handle urgent data inline */
25827750Sminshall #ifdef SO_OOBINLINE
25936276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
26027750Sminshall syslog(LOG_ERR, "setsockopt: %m");
26136276Sbostic #endif
26238134Srick
26336933Skarels #ifdef F_SETOWN
26436304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
26536304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m");
26636933Skarels #endif
26716760Slepreau dolog(&his_addr);
26816339Skarels /*
26916339Skarels * Set up default state
27016339Skarels */
27116339Skarels data = -1;
27216339Skarels type = TYPE_A;
27316339Skarels form = FORM_N;
27416339Skarels stru = STRU_F;
27516339Skarels mode = MODE_S;
27626044Sminshall tmpline[0] = '\0';
27755258Sandrew
27855258Sandrew /* If logins are disabled, print out the message. */
27955258Sandrew if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
28066711Spendry while (fgets(line, sizeof(line), fd) != NULL) {
28160087Sbostic if ((cp = strchr(line, '\n')) != NULL)
28255258Sandrew *cp = '\0';
28355258Sandrew lreply(530, "%s", line);
28455258Sandrew }
28555258Sandrew (void) fflush(stdout);
28655258Sandrew (void) fclose(fd);
28755258Sandrew reply(530, "System not available.");
28855258Sandrew exit(0);
28955258Sandrew }
29055258Sandrew if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
29166711Spendry while (fgets(line, sizeof(line), fd) != NULL) {
29260087Sbostic if ((cp = strchr(line, '\n')) != NULL)
29355258Sandrew *cp = '\0';
29455258Sandrew lreply(220, "%s", line);
29555258Sandrew }
29655258Sandrew (void) fflush(stdout);
29755258Sandrew (void) fclose(fd);
29855258Sandrew /* reply(220,) must follow */
29955258Sandrew }
30066711Spendry (void) gethostname(hostname, sizeof(hostname));
30136276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version);
30236304Skarels (void) setjmp(errcatch);
30336304Skarels for (;;)
30426493Sminshall (void) yyparse();
30536620Srick /* NOTREACHED */
30610275Ssam }
30710419Ssam
30855258Sandrew static void
lostconn(signo)30955258Sandrew lostconn(signo)
31055258Sandrew int signo;
31110275Ssam {
31266711Spendry
31314089Ssam if (debug)
31426493Sminshall syslog(LOG_DEBUG, "lost connection");
31514089Ssam dologout(-1);
31610275Ssam }
31710275Ssam
31835672Sbostic static char ttyline[20];
31935672Sbostic
32036185Sbostic /*
32136185Sbostic * Helper function for sgetpwnam().
32236185Sbostic */
32355258Sandrew static char *
sgetsave(s)32436185Sbostic sgetsave(s)
32536185Sbostic char *s;
32636185Sbostic {
32736185Sbostic char *new = malloc((unsigned) strlen(s) + 1);
32836933Skarels
32936185Sbostic if (new == NULL) {
33036620Srick perror_reply(421, "Local resource failure: malloc");
33136185Sbostic dologout(1);
33236620Srick /* NOTREACHED */
33336185Sbostic }
33436185Sbostic (void) strcpy(new, s);
33536185Sbostic return (new);
33636185Sbostic }
33736185Sbostic
33836185Sbostic /*
33936185Sbostic * Save the result of a getpwnam. Used for USER command, since
34036185Sbostic * the data returned must not be clobbered by any other command
34136185Sbostic * (e.g., globbing).
34236185Sbostic */
34355258Sandrew static struct passwd *
sgetpwnam(name)34436185Sbostic sgetpwnam(name)
34536185Sbostic char *name;
34636185Sbostic {
34736185Sbostic static struct passwd save;
34866711Spendry struct passwd *p;
34936185Sbostic
35036185Sbostic if ((p = getpwnam(name)) == NULL)
35136185Sbostic return (p);
35236185Sbostic if (save.pw_name) {
35336185Sbostic free(save.pw_name);
35436185Sbostic free(save.pw_passwd);
35536185Sbostic free(save.pw_gecos);
35636185Sbostic free(save.pw_dir);
35736185Sbostic free(save.pw_shell);
35836185Sbostic }
35936185Sbostic save = *p;
36036185Sbostic save.pw_name = sgetsave(p->pw_name);
36136185Sbostic save.pw_passwd = sgetsave(p->pw_passwd);
36236185Sbostic save.pw_gecos = sgetsave(p->pw_gecos);
36336185Sbostic save.pw_dir = sgetsave(p->pw_dir);
36436185Sbostic save.pw_shell = sgetsave(p->pw_shell);
36536185Sbostic return (&save);
36636185Sbostic }
36736185Sbostic
36855258Sandrew static int login_attempts; /* number of failed login attempts */
36955258Sandrew static int askpasswd; /* had user command, ask for passwd */
37055258Sandrew static char curname[10]; /* current USER name */
37136304Skarels
37236304Skarels /*
37336304Skarels * USER command.
37442327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable;
37542327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously,
37642327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in
37742327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
37842327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user
37942327Sbostic * requesting login privileges. Disallow anyone who does not have a standard
38042327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file
38142327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
38236304Skarels */
38355258Sandrew void
user(name)38436304Skarels user(name)
38536304Skarels char *name;
38636304Skarels {
38766711Spendry char *cp, *shell;
38836304Skarels
38936304Skarels if (logged_in) {
39036304Skarels if (guest) {
39136304Skarels reply(530, "Can't change user from guest login.");
39236304Skarels return;
39336304Skarels }
39436304Skarels end_login();
39536304Skarels }
39636304Skarels
39736304Skarels guest = 0;
39836304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
39940183Smckusick if (checkuser("ftp") || checkuser("anonymous"))
40040155Smckusick reply(530, "User %s access denied.", name);
40140155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) {
40236304Skarels guest = 1;
40336304Skarels askpasswd = 1;
40455258Sandrew reply(331,
40555258Sandrew "Guest login ok, type your name as password.");
40636933Skarels } else
40736304Skarels reply(530, "User %s unknown.", name);
40855258Sandrew if (!askpasswd && logging)
40955258Sandrew syslog(LOG_NOTICE,
41055258Sandrew "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
41136304Skarels return;
41236304Skarels }
41336304Skarels if (pw = sgetpwnam(name)) {
41436304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0)
41537459Skarels shell = _PATH_BSHELL;
41636304Skarels while ((cp = getusershell()) != NULL)
41736304Skarels if (strcmp(cp, shell) == 0)
41836304Skarels break;
41936304Skarels endusershell();
42055258Sandrew
42140183Smckusick if (cp == NULL || checkuser(name)) {
42236304Skarels reply(530, "User %s access denied.", name);
42336933Skarels if (logging)
42436933Skarels syslog(LOG_NOTICE,
42536933Skarels "FTP LOGIN REFUSED FROM %s, %s",
42636933Skarels remotehost, name);
42736304Skarels pw = (struct passwd *) NULL;
42836304Skarels return;
42936304Skarels }
43036304Skarels }
43155258Sandrew if (logging)
43255258Sandrew strncpy(curname, name, sizeof(curname)-1);
43336304Skarels reply(331, "Password required for %s.", name);
43436304Skarels askpasswd = 1;
43536304Skarels /*
43636304Skarels * Delay before reading passwd after first failed
43736304Skarels * attempt to slow down passwd-guessing programs.
43836304Skarels */
43936304Skarels if (login_attempts)
44036304Skarels sleep((unsigned) login_attempts);
44136304Skarels }
44236304Skarels
44336304Skarels /*
44440183Smckusick * Check if a user is in the file _PATH_FTPUSERS
44540183Smckusick */
44655258Sandrew static int
checkuser(name)44740183Smckusick checkuser(name)
44840183Smckusick char *name;
44940183Smckusick {
45066711Spendry FILE *fd;
45155258Sandrew int found = 0;
45266711Spendry char *p, line[BUFSIZ];
45340183Smckusick
45440183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
45542327Sbostic while (fgets(line, sizeof(line), fd) != NULL)
45660087Sbostic if ((p = strchr(line, '\n')) != NULL) {
45742327Sbostic *p = '\0';
45842327Sbostic if (line[0] == '#')
45942327Sbostic continue;
460*68976Sbostic if (strcmp(line, name) == 0) {
46155258Sandrew found = 1;
46255258Sandrew break;
46355258Sandrew }
46442327Sbostic }
46540183Smckusick (void) fclose(fd);
46640183Smckusick }
46755258Sandrew return (found);
46840183Smckusick }
46940183Smckusick
47040183Smckusick /*
47136304Skarels * Terminate login as previous user, if any, resetting state;
47236304Skarels * used when USER command is given or login fails.
47336304Skarels */
47455258Sandrew static void
end_login()47536304Skarels end_login()
47636304Skarels {
47736304Skarels
47836304Skarels (void) seteuid((uid_t)0);
47936304Skarels if (logged_in)
48036304Skarels logwtmp(ttyline, "", "");
48136304Skarels pw = NULL;
48236304Skarels logged_in = 0;
48336304Skarels guest = 0;
48436304Skarels }
48536304Skarels
48655258Sandrew void
pass(passwd)48710275Ssam pass(passwd)
48810275Ssam char *passwd;
48910275Ssam {
49066711Spendry char *salt, *xpasswd;
49155258Sandrew FILE *fd;
49210275Ssam
49336304Skarels if (logged_in || askpasswd == 0) {
49410275Ssam reply(503, "Login with USER first.");
49510275Ssam return;
49610275Ssam }
49736304Skarels askpasswd = 0;
49810275Ssam if (!guest) { /* "ftp" is only account allowed no password */
49936304Skarels if (pw == NULL)
50036304Skarels salt = "xx";
50136304Skarels else
50236304Skarels salt = pw->pw_passwd;
50336304Skarels xpasswd = crypt(passwd, salt);
50416760Slepreau /* The strcmp does not catch null passwords! */
50536304Skarels if (pw == NULL || *pw->pw_passwd == '\0' ||
50636304Skarels strcmp(xpasswd, pw->pw_passwd)) {
50710275Ssam reply(530, "Login incorrect.");
50855258Sandrew if (logging)
50955258Sandrew syslog(LOG_NOTICE,
51055258Sandrew "FTP LOGIN FAILED FROM %s, %s",
51155258Sandrew remotehost, curname);
51210275Ssam pw = NULL;
51336304Skarels if (login_attempts++ >= 5) {
51436933Skarels syslog(LOG_NOTICE,
51536304Skarels "repeated login failures from %s",
51636304Skarels remotehost);
51736304Skarels exit(0);
51836304Skarels }
51910275Ssam return;
52010275Ssam }
52110275Ssam }
52236304Skarels login_attempts = 0; /* this time successful */
52355258Sandrew if (setegid((gid_t)pw->pw_gid) < 0) {
52455258Sandrew reply(550, "Can't set gid.");
52555258Sandrew return;
52655258Sandrew }
52736304Skarels (void) initgroups(pw->pw_name, pw->pw_gid);
52816033Sralph
52936192Sbostic /* open wtmp before chroot */
53036192Sbostic (void)sprintf(ttyline, "ftp%d", getpid());
53136192Sbostic logwtmp(ttyline, pw->pw_name, remotehost);
53236192Sbostic logged_in = 1;
53336192Sbostic
53436446Sbostic if (guest) {
53536620Srick /*
53636933Skarels * We MUST do a chdir() after the chroot. Otherwise
53736933Skarels * the old current directory will be accessible as "."
53836933Skarels * outside the new root!
53936620Srick */
54036620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
54136446Sbostic reply(550, "Can't set guest privileges.");
54236446Sbostic goto bad;
54336446Sbostic }
54436933Skarels } else if (chdir(pw->pw_dir) < 0) {
54536620Srick if (chdir("/") < 0) {
54636446Sbostic reply(530, "User %s: can't change directory to %s.",
54736446Sbostic pw->pw_name, pw->pw_dir);
54836446Sbostic goto bad;
54936933Skarels } else
55036446Sbostic lreply(230, "No directory! Logging in with home=/");
55136620Srick }
55236304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) {
55336304Skarels reply(550, "Can't set uid.");
55436304Skarels goto bad;
55536304Skarels }
55655258Sandrew /*
55755258Sandrew * Display a login message, if it exists.
55855258Sandrew * N.B. reply(230,) must follow the message.
55955258Sandrew */
56055258Sandrew if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
56166711Spendry char *cp, line[LINE_MAX];
56255258Sandrew
56366711Spendry while (fgets(line, sizeof(line), fd) != NULL) {
56460087Sbostic if ((cp = strchr(line, '\n')) != NULL)
56555258Sandrew *cp = '\0';
56655258Sandrew lreply(230, "%s", line);
56755258Sandrew }
56855258Sandrew (void) fflush(stdout);
56955258Sandrew (void) fclose(fd);
57055258Sandrew }
57136550Sbostic if (guest) {
57236192Sbostic reply(230, "Guest login ok, access restrictions apply.");
57336933Skarels #ifdef SETPROCTITLE
57466711Spendry snprintf(proctitle, sizeof(proctitle),
57566711Spendry "%s: anonymous/%.*s", remotehost,
57636933Skarels sizeof(proctitle) - sizeof(remotehost) -
57736933Skarels sizeof(": anonymous/"), passwd);
57836933Skarels setproctitle(proctitle);
57936933Skarels #endif /* SETPROCTITLE */
58036933Skarels if (logging)
58136933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
58236933Skarels remotehost, passwd);
58336933Skarels } else {
58410275Ssam reply(230, "User %s logged in.", pw->pw_name);
58536933Skarels #ifdef SETPROCTITLE
58666711Spendry snprintf(proctitle, sizeof(proctitle),
58766711Spendry "%s: %s", remotehost, pw->pw_name);
58836933Skarels setproctitle(proctitle);
58936933Skarels #endif /* SETPROCTITLE */
59036933Skarels if (logging)
59155258Sandrew syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
59236933Skarels remotehost, pw->pw_name);
59336550Sbostic }
59436933Skarels (void) umask(defumask);
59510303Ssam return;
59610303Ssam bad:
59736304Skarels /* Forget all about it... */
59836304Skarels end_login();
59910275Ssam }
60010275Ssam
60155258Sandrew void
retrieve(cmd,name)60210275Ssam retrieve(cmd, name)
60310275Ssam char *cmd, *name;
60410275Ssam {
60510275Ssam FILE *fin, *dout;
60610275Ssam struct stat st;
60766711Spendry int (*closefunc) __P((FILE *));
60810275Ssam
60936557Sbostic if (cmd == 0) {
61036446Sbostic fin = fopen(name, "r"), closefunc = fclose;
61136557Sbostic st.st_size = 0;
61236557Sbostic } else {
61310275Ssam char line[BUFSIZ];
61410275Ssam
61526493Sminshall (void) sprintf(line, cmd, name), name = line;
61636304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
61736557Sbostic st.st_size = -1;
61836933Skarels st.st_blksize = BUFSIZ;
61910275Ssam }
62010275Ssam if (fin == NULL) {
62155258Sandrew if (errno != 0) {
62236304Skarels perror_reply(550, name);
62355258Sandrew if (cmd == 0) {
62455258Sandrew LOGCMD("get", name);
62555258Sandrew }
62655258Sandrew }
62710275Ssam return;
62810275Ssam }
62955258Sandrew byte_count = -1;
63066711Spendry if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
63110275Ssam reply(550, "%s: not a plain file.", name);
63210275Ssam goto done;
63310275Ssam }
63437459Skarels if (restart_point) {
63537459Skarels if (type == TYPE_A) {
63666711Spendry off_t i, n;
63766711Spendry int c;
63837459Skarels
63937459Skarels n = restart_point;
64037459Skarels i = 0;
64137459Skarels while (i++ < n) {
64237459Skarels if ((c=getc(fin)) == EOF) {
64337459Skarels perror_reply(550, name);
64437459Skarels goto done;
64537459Skarels }
64637459Skarels if (c == '\n')
64737459Skarels i++;
64852998Sbostic }
64937459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
65037459Skarels perror_reply(550, name);
65137459Skarels goto done;
65237459Skarels }
65337459Skarels }
65410275Ssam dout = dataconn(name, st.st_size, "w");
65510275Ssam if (dout == NULL)
65610275Ssam goto done;
65736620Srick send_data(fin, dout, st.st_blksize);
65826493Sminshall (void) fclose(dout);
65926044Sminshall data = -1;
66026044Sminshall pdata = -1;
66110275Ssam done:
66255258Sandrew if (cmd == 0)
66355258Sandrew LOGBYTES("get", name, byte_count);
66410275Ssam (*closefunc)(fin);
66510275Ssam }
66610275Ssam
66755258Sandrew void
store(name,mode,unique)66836304Skarels store(name, mode, unique)
66910275Ssam char *name, *mode;
67036304Skarels int unique;
67110275Ssam {
67210275Ssam FILE *fout, *din;
67336446Sbostic struct stat st;
67466711Spendry int (*closefunc) __P((FILE *));
67510275Ssam
67636446Sbostic if (unique && stat(name, &st) == 0 &&
67755258Sandrew (name = gunique(name)) == NULL) {
67855258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name);
67936446Sbostic return;
68055258Sandrew }
68110303Ssam
68237459Skarels if (restart_point)
68366711Spendry mode = "r+";
68436620Srick fout = fopen(name, mode);
68536620Srick closefunc = fclose;
68610275Ssam if (fout == NULL) {
68736304Skarels perror_reply(553, name);
68855258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name);
68910275Ssam return;
69010275Ssam }
69155258Sandrew byte_count = -1;
69237459Skarels if (restart_point) {
69337459Skarels if (type == TYPE_A) {
69466711Spendry off_t i, n;
69566711Spendry int c;
69637459Skarels
69737459Skarels n = restart_point;
69837459Skarels i = 0;
69937459Skarels while (i++ < n) {
70037459Skarels if ((c=getc(fout)) == EOF) {
70137459Skarels perror_reply(550, name);
70237459Skarels goto done;
70337459Skarels }
70437459Skarels if (c == '\n')
70537459Skarels i++;
70652998Sbostic }
70737459Skarels /*
70837459Skarels * We must do this seek to "current" position
70937459Skarels * because we are changing from reading to
71037459Skarels * writing.
71137459Skarels */
71237459Skarels if (fseek(fout, 0L, L_INCR) < 0) {
71337459Skarels perror_reply(550, name);
71437459Skarels goto done;
71537459Skarels }
71637459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
71737459Skarels perror_reply(550, name);
71837459Skarels goto done;
71937459Skarels }
72037459Skarels }
72136304Skarels din = dataconn(name, (off_t)-1, "r");
72210275Ssam if (din == NULL)
72310275Ssam goto done;
72436620Srick if (receive_data(din, fout) == 0) {
72536620Srick if (unique)
72636304Skarels reply(226, "Transfer complete (unique file name:%s).",
72736933Skarels name);
72836304Skarels else
72936304Skarels reply(226, "Transfer complete.");
73026044Sminshall }
73126493Sminshall (void) fclose(din);
73226044Sminshall data = -1;
73326044Sminshall pdata = -1;
73410275Ssam done:
73555258Sandrew LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
73610275Ssam (*closefunc)(fout);
73710275Ssam }
73810275Ssam
73955258Sandrew static FILE *
getdatasock(mode)74010275Ssam getdatasock(mode)
74110275Ssam char *mode;
74210275Ssam {
74366711Spendry int on = 1, s, t, tries;
74410275Ssam
74510275Ssam if (data >= 0)
74610275Ssam return (fdopen(data, mode));
74750391Skarels (void) seteuid((uid_t)0);
74813247Ssam s = socket(AF_INET, SOCK_STREAM, 0);
74910602Ssam if (s < 0)
75050391Skarels goto bad;
75137459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
75266711Spendry (char *) &on, sizeof(on)) < 0)
75310602Ssam goto bad;
75413152Ssam /* anchor socket to avoid multi-homing problems */
75513152Ssam data_source.sin_family = AF_INET;
75613152Ssam data_source.sin_addr = ctrl_addr.sin_addr;
75737459Skarels for (tries = 1; ; tries++) {
75837459Skarels if (bind(s, (struct sockaddr *)&data_source,
75966711Spendry sizeof(data_source)) >= 0)
76037459Skarels break;
76137459Skarels if (errno != EADDRINUSE || tries > 10)
76237459Skarels goto bad;
76337459Skarels sleep(tries);
76437459Skarels }
76536304Skarels (void) seteuid((uid_t)pw->pw_uid);
76644339Skarels #ifdef IP_TOS
76744339Skarels on = IPTOS_THROUGHPUT;
76844339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
76944339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
77044339Skarels #endif
77110275Ssam return (fdopen(s, mode));
77210602Ssam bad:
77355258Sandrew /* Return the real value of errno (close may change it) */
77455258Sandrew t = errno;
77536304Skarels (void) seteuid((uid_t)pw->pw_uid);
77626493Sminshall (void) close(s);
77755258Sandrew errno = t;
77810602Ssam return (NULL);
77910275Ssam }
78010275Ssam
78155258Sandrew static FILE *
dataconn(name,size,mode)78210275Ssam dataconn(name, size, mode)
78310275Ssam char *name;
78411653Ssam off_t size;
78510275Ssam char *mode;
78610275Ssam {
78710275Ssam char sizebuf[32];
78810275Ssam FILE *file;
78944339Skarels int retry = 0, tos;
79010275Ssam
79136933Skarels file_size = size;
79236933Skarels byte_count = 0;
79336304Skarels if (size != (off_t) -1)
79466711Spendry (void) sprintf(sizebuf, " (%qd bytes)", size);
79510275Ssam else
79610275Ssam (void) strcpy(sizebuf, "");
79736304Skarels if (pdata >= 0) {
79826044Sminshall struct sockaddr_in from;
79926044Sminshall int s, fromlen = sizeof(from);
80026044Sminshall
80136304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen);
80226044Sminshall if (s < 0) {
80326044Sminshall reply(425, "Can't open data connection.");
80426044Sminshall (void) close(pdata);
80526044Sminshall pdata = -1;
80666711Spendry return (NULL);
80726044Sminshall }
80826044Sminshall (void) close(pdata);
80926044Sminshall pdata = s;
81044339Skarels #ifdef IP_TOS
81144339Skarels tos = IPTOS_LOWDELAY;
81244339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
81344339Skarels sizeof(int));
81444339Skarels #endif
81555258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.",
81636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
81766711Spendry return (fdopen(pdata, mode));
81826044Sminshall }
81910275Ssam if (data >= 0) {
82055258Sandrew reply(125, "Using existing data connection for '%s'%s.",
82110275Ssam name, sizebuf);
82210321Ssam usedefault = 1;
82310275Ssam return (fdopen(data, mode));
82410275Ssam }
82510566Ssam if (usedefault)
82610422Ssam data_dest = his_addr;
82710422Ssam usedefault = 1;
82810275Ssam file = getdatasock(mode);
82910275Ssam if (file == NULL) {
83010275Ssam reply(425, "Can't create data socket (%s,%d): %s.",
83113247Ssam inet_ntoa(data_source.sin_addr),
83242412Sbostic ntohs(data_source.sin_port), strerror(errno));
83310275Ssam return (NULL);
83410275Ssam }
83510275Ssam data = fileno(file);
83636304Skarels while (connect(data, (struct sockaddr *)&data_dest,
83766711Spendry sizeof(data_dest)) < 0) {
83811653Ssam if (errno == EADDRINUSE && retry < swaitmax) {
83926493Sminshall sleep((unsigned) swaitint);
84011653Ssam retry += swaitint;
84111653Ssam continue;
84211653Ssam }
84336304Skarels perror_reply(425, "Can't build data connection");
84410275Ssam (void) fclose(file);
84510275Ssam data = -1;
84610275Ssam return (NULL);
84710275Ssam }
84855258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.",
84936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
85010275Ssam return (file);
85110275Ssam }
85210275Ssam
85310275Ssam /*
85455258Sandrew * Tranfer the contents of "instr" to "outstr" peer using the appropriate
85555258Sandrew * encapsulation of the data subject * to Mode, Structure, and Type.
85610275Ssam *
85710275Ssam * NB: Form isn't handled.
85810275Ssam */
85955258Sandrew static void
send_data(instr,outstr,blksize)86036446Sbostic send_data(instr, outstr, blksize)
86110275Ssam FILE *instr, *outstr;
86236446Sbostic off_t blksize;
86310275Ssam {
86466711Spendry int c, cnt, filefd, netfd;
86566711Spendry char *buf;
86610275Ssam
86726044Sminshall transflag++;
86826044Sminshall if (setjmp(urgcatch)) {
86926044Sminshall transflag = 0;
87036620Srick return;
87126044Sminshall }
87210275Ssam switch (type) {
87310275Ssam
87410275Ssam case TYPE_A:
87510275Ssam while ((c = getc(instr)) != EOF) {
87636933Skarels byte_count++;
87711220Ssam if (c == '\n') {
87836933Skarels if (ferror(outstr))
87936620Srick goto data_err;
88027750Sminshall (void) putc('\r', outstr);
88111220Ssam }
88227750Sminshall (void) putc(c, outstr);
88310275Ssam }
88436933Skarels fflush(outstr);
88526044Sminshall transflag = 0;
88636933Skarels if (ferror(instr))
88736933Skarels goto file_err;
88836933Skarels if (ferror(outstr))
88936620Srick goto data_err;
89036620Srick reply(226, "Transfer complete.");
89136620Srick return;
89236446Sbostic
89310275Ssam case TYPE_I:
89410275Ssam case TYPE_L:
89536446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) {
89636446Sbostic transflag = 0;
89736933Skarels perror_reply(451, "Local resource failure: malloc");
89836933Skarels return;
89936446Sbostic }
90010275Ssam netfd = fileno(outstr);
90110275Ssam filefd = fileno(instr);
90236933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
90336620Srick write(netfd, buf, cnt) == cnt)
90436933Skarels byte_count += cnt;
90526044Sminshall transflag = 0;
90636446Sbostic (void)free(buf);
90736933Skarels if (cnt != 0) {
90836933Skarels if (cnt < 0)
90936933Skarels goto file_err;
91036620Srick goto data_err;
91136933Skarels }
91236620Srick reply(226, "Transfer complete.");
91336620Srick return;
91436620Srick default:
91536620Srick transflag = 0;
91636620Srick reply(550, "Unimplemented TYPE %d in send_data", type);
91736620Srick return;
91810275Ssam }
91936620Srick
92036620Srick data_err:
92126044Sminshall transflag = 0;
92236933Skarels perror_reply(426, "Data connection");
92336933Skarels return;
92436933Skarels
92536933Skarels file_err:
92636933Skarels transflag = 0;
92736933Skarels perror_reply(551, "Error on input file");
92810275Ssam }
92910275Ssam
93010275Ssam /*
93155258Sandrew * Transfer data from peer to "outstr" using the appropriate encapulation of
93255258Sandrew * the data subject to Mode, Structure, and Type.
93310275Ssam *
93410275Ssam * N.B.: Form isn't handled.
93510275Ssam */
93655258Sandrew static int
receive_data(instr,outstr)93710275Ssam receive_data(instr, outstr)
93810275Ssam FILE *instr, *outstr;
93910275Ssam {
94066711Spendry int c;
94138134Srick int cnt, bare_lfs = 0;
94210275Ssam char buf[BUFSIZ];
94310275Ssam
94426044Sminshall transflag++;
94526044Sminshall if (setjmp(urgcatch)) {
94626044Sminshall transflag = 0;
94736933Skarels return (-1);
94826044Sminshall }
94910275Ssam switch (type) {
95010275Ssam
95110275Ssam case TYPE_I:
95210275Ssam case TYPE_L:
95366711Spendry while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
95436620Srick if (write(fileno(outstr), buf, cnt) != cnt)
95536933Skarels goto file_err;
95636933Skarels byte_count += cnt;
95726044Sminshall }
95836933Skarels if (cnt < 0)
95936620Srick goto data_err;
96026044Sminshall transflag = 0;
96136933Skarels return (0);
96210275Ssam
96310275Ssam case TYPE_E:
96427106Smckusick reply(553, "TYPE E not implemented.");
96526044Sminshall transflag = 0;
96627106Smckusick return (-1);
96710275Ssam
96810275Ssam case TYPE_A:
96910275Ssam while ((c = getc(instr)) != EOF) {
97036933Skarels byte_count++;
97138134Srick if (c == '\n')
97238134Srick bare_lfs++;
97327750Sminshall while (c == '\r') {
97436933Skarels if (ferror(outstr))
97536620Srick goto data_err;
97636933Skarels if ((c = getc(instr)) != '\n') {
97727750Sminshall (void) putc ('\r', outstr);
97836933Skarels if (c == '\0' || c == EOF)
97936933Skarels goto contin2;
98036933Skarels }
98110275Ssam }
98236933Skarels (void) putc(c, outstr);
98336933Skarels contin2: ;
98410275Ssam }
98536620Srick fflush(outstr);
98636933Skarels if (ferror(instr))
98736620Srick goto data_err;
98836933Skarels if (ferror(outstr))
98936933Skarels goto file_err;
99026044Sminshall transflag = 0;
99138134Srick if (bare_lfs) {
99266842Sbostic lreply(226,
99366842Sbostic "WARNING! %d bare linefeeds received in ASCII mode",
99466842Sbostic bare_lfs);
99566842Sbostic (void)printf(" File may not have transferred correctly.\r\n");
99638134Srick }
99710275Ssam return (0);
99836620Srick default:
99936620Srick reply(550, "Unimplemented TYPE %d in receive_data", type);
100036620Srick transflag = 0;
100136933Skarels return (-1);
100210275Ssam }
100336620Srick
100436620Srick data_err:
100526044Sminshall transflag = 0;
100636933Skarels perror_reply(426, "Data Connection");
100736933Skarels return (-1);
100836933Skarels
100936933Skarels file_err:
101036933Skarels transflag = 0;
101136933Skarels perror_reply(452, "Error writing file");
101236933Skarels return (-1);
101310275Ssam }
101410275Ssam
101555258Sandrew void
statfilecmd(filename)101636933Skarels statfilecmd(filename)
101736933Skarels char *filename;
101836933Skarels {
101936933Skarels FILE *fin;
102036933Skarels int c;
102166711Spendry char line[LINE_MAX];
102236933Skarels
102355258Sandrew (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
102436933Skarels fin = ftpd_popen(line, "r");
102536933Skarels lreply(211, "status of %s:", filename);
102636933Skarels while ((c = getc(fin)) != EOF) {
102736933Skarels if (c == '\n') {
102836933Skarels if (ferror(stdout)){
102936933Skarels perror_reply(421, "control connection");
103036933Skarels (void) ftpd_pclose(fin);
103136933Skarels dologout(1);
103236933Skarels /* NOTREACHED */
103336933Skarels }
103436933Skarels if (ferror(fin)) {
103536933Skarels perror_reply(551, filename);
103636933Skarels (void) ftpd_pclose(fin);
103736933Skarels return;
103836933Skarels }
103936933Skarels (void) putc('\r', stdout);
104036933Skarels }
104136933Skarels (void) putc(c, stdout);
104236933Skarels }
104336933Skarels (void) ftpd_pclose(fin);
104436933Skarels reply(211, "End of Status");
104536933Skarels }
104636933Skarels
104755258Sandrew void
statcmd()104836933Skarels statcmd()
104936933Skarels {
105036933Skarels struct sockaddr_in *sin;
105136933Skarels u_char *a, *p;
105236933Skarels
105336933Skarels lreply(211, "%s FTP server status:", hostname, version);
105436933Skarels printf(" %s\r\n", version);
105536933Skarels printf(" Connected to %s", remotehost);
105638134Srick if (!isdigit(remotehost[0]))
105736933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr));
105836933Skarels printf("\r\n");
105936933Skarels if (logged_in) {
106036933Skarels if (guest)
106136933Skarels printf(" Logged in anonymously\r\n");
106236933Skarels else
106336933Skarels printf(" Logged in as %s\r\n", pw->pw_name);
106436933Skarels } else if (askpasswd)
106536933Skarels printf(" Waiting for password\r\n");
106636933Skarels else
106736933Skarels printf(" Waiting for user name\r\n");
106836933Skarels printf(" TYPE: %s", typenames[type]);
106936933Skarels if (type == TYPE_A || type == TYPE_E)
107036933Skarels printf(", FORM: %s", formnames[form]);
107136933Skarels if (type == TYPE_L)
107236933Skarels #if NBBY == 8
107336933Skarels printf(" %d", NBBY);
107436933Skarels #else
107536933Skarels printf(" %d", bytesize); /* need definition! */
107636933Skarels #endif
107736933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n",
107836933Skarels strunames[stru], modenames[mode]);
107936933Skarels if (data != -1)
108036933Skarels printf(" Data connection open\r\n");
108136933Skarels else if (pdata != -1) {
108236933Skarels printf(" in Passive mode");
108336933Skarels sin = &pasv_addr;
108436933Skarels goto printaddr;
108536933Skarels } else if (usedefault == 0) {
108636933Skarels printf(" PORT");
108736933Skarels sin = &data_dest;
108836933Skarels printaddr:
108936933Skarels a = (u_char *) &sin->sin_addr;
109036933Skarels p = (u_char *) &sin->sin_port;
109136933Skarels #define UC(b) (((int) b) & 0xff)
109236933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
109336933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
109436933Skarels #undef UC
109536933Skarels } else
109636933Skarels printf(" No data connection\r\n");
109736933Skarels reply(211, "End of status");
109836933Skarels }
109936933Skarels
110055258Sandrew void
fatal(s)110110275Ssam fatal(s)
110210275Ssam char *s;
110310275Ssam {
110466711Spendry
110510275Ssam reply(451, "Error in server: %s\n", s);
110610275Ssam reply(221, "Closing connection due to server error.");
110713247Ssam dologout(0);
110836620Srick /* NOTREACHED */
110910275Ssam }
111010275Ssam
111155258Sandrew void
111255258Sandrew #if __STDC__
reply(int n,const char * fmt,...)111355258Sandrew reply(int n, const char *fmt, ...)
111455258Sandrew #else
111555258Sandrew reply(n, fmt, va_alist)
111610275Ssam int n;
111736446Sbostic char *fmt;
111855258Sandrew va_dcl
111955258Sandrew #endif
112010275Ssam {
112155258Sandrew va_list ap;
112255258Sandrew #if __STDC__
112355258Sandrew va_start(ap, fmt);
112455258Sandrew #else
112555258Sandrew va_start(ap);
112655258Sandrew #endif
112755258Sandrew (void)printf("%d ", n);
112855258Sandrew (void)vprintf(fmt, ap);
112955258Sandrew (void)printf("\r\n");
113036435Sbostic (void)fflush(stdout);
113110275Ssam if (debug) {
113226493Sminshall syslog(LOG_DEBUG, "<--- %d ", n);
113355258Sandrew vsyslog(LOG_DEBUG, fmt, ap);
113455258Sandrew }
113510275Ssam }
113610275Ssam
113755258Sandrew void
113855258Sandrew #if __STDC__
lreply(int n,const char * fmt,...)113955258Sandrew lreply(int n, const char *fmt, ...)
114055258Sandrew #else
114155258Sandrew lreply(n, fmt, va_alist)
114210275Ssam int n;
114336446Sbostic char *fmt;
114455258Sandrew va_dcl
114555258Sandrew #endif
114610275Ssam {
114755258Sandrew va_list ap;
114855258Sandrew #if __STDC__
114955258Sandrew va_start(ap, fmt);
115055258Sandrew #else
115155258Sandrew va_start(ap);
115255258Sandrew #endif
115355258Sandrew (void)printf("%d- ", n);
115455258Sandrew (void)vprintf(fmt, ap);
115555258Sandrew (void)printf("\r\n");
115636435Sbostic (void)fflush(stdout);
115736446Sbostic if (debug) {
115836446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n);
115955258Sandrew vsyslog(LOG_DEBUG, fmt, ap);
116036446Sbostic }
116110275Ssam }
116210275Ssam
116355258Sandrew static void
ack(s)116410275Ssam ack(s)
116510275Ssam char *s;
116610275Ssam {
116766711Spendry
116827106Smckusick reply(250, "%s command successful.", s);
116910275Ssam }
117010275Ssam
117155258Sandrew void
nack(s)117210275Ssam nack(s)
117310275Ssam char *s;
117410275Ssam {
117566711Spendry
117610275Ssam reply(502, "%s command not implemented.", s);
117710275Ssam }
117810275Ssam
117936304Skarels /* ARGSUSED */
118066711Spendry void
yyerror(s)118126493Sminshall yyerror(s)
118226493Sminshall char *s;
118310275Ssam {
118426044Sminshall char *cp;
118526044Sminshall
118660087Sbostic if (cp = strchr(cbuf,'\n'))
118736551Sbostic *cp = '\0';
118836933Skarels reply(500, "'%s': command not understood.", cbuf);
118910275Ssam }
119010275Ssam
119155258Sandrew void
delete(name)119210275Ssam delete(name)
119310275Ssam char *name;
119410275Ssam {
119510275Ssam struct stat st;
119610275Ssam
119755258Sandrew LOGCMD("delete", name);
119810275Ssam if (stat(name, &st) < 0) {
119936304Skarels perror_reply(550, name);
120010275Ssam return;
120110275Ssam }
120210275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) {
120310275Ssam if (rmdir(name) < 0) {
120436304Skarels perror_reply(550, name);
120510275Ssam return;
120610275Ssam }
120710275Ssam goto done;
120810275Ssam }
120910275Ssam if (unlink(name) < 0) {
121036304Skarels perror_reply(550, name);
121110275Ssam return;
121210275Ssam }
121310275Ssam done:
121410275Ssam ack("DELE");
121510275Ssam }
121610275Ssam
121755258Sandrew void
cwd(path)121810275Ssam cwd(path)
121910275Ssam char *path;
122010275Ssam {
122166711Spendry
122236620Srick if (chdir(path) < 0)
122336304Skarels perror_reply(550, path);
122436620Srick else
122536620Srick ack("CWD");
122610275Ssam }
122710275Ssam
122855258Sandrew void
makedir(name)122910303Ssam makedir(name)
123010275Ssam char *name;
123110275Ssam {
123266711Spendry
123355258Sandrew LOGCMD("mkdir", name);
123436276Sbostic if (mkdir(name, 0777) < 0)
123536304Skarels perror_reply(550, name);
123636276Sbostic else
123736276Sbostic reply(257, "MKD command successful.");
123810275Ssam }
123910275Ssam
124055258Sandrew void
removedir(name)124110303Ssam removedir(name)
124210275Ssam char *name;
124310275Ssam {
124466711Spendry
124555258Sandrew LOGCMD("rmdir", name);
124636620Srick if (rmdir(name) < 0)
124736304Skarels perror_reply(550, name);
124836620Srick else
124936620Srick ack("RMD");
125010275Ssam }
125110275Ssam
125255258Sandrew void
pwd()125310303Ssam pwd()
125410275Ssam {
125510303Ssam char path[MAXPATHLEN + 1];
125610275Ssam
125736620Srick if (getwd(path) == (char *)NULL)
125827106Smckusick reply(550, "%s.", path);
125936620Srick else
126036620Srick reply(257, "\"%s\" is current directory.", path);
126110275Ssam }
126210275Ssam
126310275Ssam char *
renamefrom(name)126410275Ssam renamefrom(name)
126510275Ssam char *name;
126610275Ssam {
126710275Ssam struct stat st;
126810275Ssam
126910275Ssam if (stat(name, &st) < 0) {
127036304Skarels perror_reply(550, name);
127110275Ssam return ((char *)0);
127210275Ssam }
127310303Ssam reply(350, "File exists, ready for destination name");
127410275Ssam return (name);
127510275Ssam }
127610275Ssam
127755258Sandrew void
renamecmd(from,to)127810275Ssam renamecmd(from, to)
127910275Ssam char *from, *to;
128010275Ssam {
128166711Spendry
128255258Sandrew LOGCMD2("rename", from, to);
128336620Srick if (rename(from, to) < 0)
128436304Skarels perror_reply(550, "rename");
128536620Srick else
128636620Srick ack("RNTO");
128710275Ssam }
128810275Ssam
128955258Sandrew static void
dolog(sin)129010275Ssam dolog(sin)
129110275Ssam struct sockaddr_in *sin;
129210275Ssam {
129336304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
129466711Spendry sizeof(struct in_addr), AF_INET);
129510275Ssam
129636304Skarels if (hp)
129766711Spendry (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
129836304Skarels else
129926493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
130066711Spendry sizeof(remotehost));
130136620Srick #ifdef SETPROCTITLE
130266711Spendry snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
130336933Skarels setproctitle(proctitle);
130436620Srick #endif /* SETPROCTITLE */
130536933Skarels
130652998Sbostic if (logging)
130752998Sbostic syslog(LOG_INFO, "connection from %s", remotehost);
130810275Ssam }
130910695Ssam
131010695Ssam /*
131113247Ssam * Record logout in wtmp file
131213247Ssam * and exit with supplied status.
131313247Ssam */
131455258Sandrew void
dologout(status)131513247Ssam dologout(status)
131613247Ssam int status;
131713247Ssam {
131866711Spendry
131917580Ssam if (logged_in) {
132036304Skarels (void) seteuid((uid_t)0);
132135672Sbostic logwtmp(ttyline, "", "");
132213247Ssam }
132314436Ssam /* beware of flushing buffers after a SIGPIPE */
132414436Ssam _exit(status);
132513247Ssam }
132613247Ssam
132755258Sandrew static void
myoob(signo)132855258Sandrew myoob(signo)
132955258Sandrew int signo;
133026044Sminshall {
133127750Sminshall char *cp;
133226044Sminshall
133327750Sminshall /* only process if transfer occurring */
133436304Skarels if (!transflag)
133526044Sminshall return;
133627750Sminshall cp = tmpline;
133727750Sminshall if (getline(cp, 7, stdin) == NULL) {
133836304Skarels reply(221, "You could at least say goodbye.");
133927750Sminshall dologout(0);
134026044Sminshall }
134126044Sminshall upper(cp);
134236933Skarels if (strcmp(cp, "ABOR\r\n") == 0) {
134336933Skarels tmpline[0] = '\0';
134436933Skarels reply(426, "Transfer aborted. Data connection closed.");
134536933Skarels reply(226, "Abort successful");
134636933Skarels longjmp(urgcatch, 1);
134736933Skarels }
134836933Skarels if (strcmp(cp, "STAT\r\n") == 0) {
134936933Skarels if (file_size != (off_t) -1)
135055258Sandrew reply(213, "Status: %qd of %qd bytes transferred",
135136933Skarels byte_count, file_size);
135236933Skarels else
135355258Sandrew reply(213, "Status: %qd bytes transferred", byte_count);
135436933Skarels }
135526044Sminshall }
135626044Sminshall
135727106Smckusick /*
135836620Srick * Note: a response of 425 is not mentioned as a possible response to
135952998Sbostic * the PASV command in RFC959. However, it has been blessed as
136052998Sbostic * a legitimate response by Jon Postel in a telephone conversation
136136620Srick * with Rick Adams on 25 Jan 89.
136227106Smckusick */
136355258Sandrew void
passive()136426044Sminshall passive()
136526044Sminshall {
136626044Sminshall int len;
136766711Spendry char *p, *a;
136826044Sminshall
136926044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0);
137026044Sminshall if (pdata < 0) {
137136620Srick perror_reply(425, "Can't open passive connection");
137226044Sminshall return;
137326044Sminshall }
137436933Skarels pasv_addr = ctrl_addr;
137536933Skarels pasv_addr.sin_port = 0;
137636304Skarels (void) seteuid((uid_t)0);
137736933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
137836304Skarels (void) seteuid((uid_t)pw->pw_uid);
137936620Srick goto pasv_error;
138026044Sminshall }
138136304Skarels (void) seteuid((uid_t)pw->pw_uid);
138236933Skarels len = sizeof(pasv_addr);
138336933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
138436620Srick goto pasv_error;
138536620Srick if (listen(pdata, 1) < 0)
138636620Srick goto pasv_error;
138736933Skarels a = (char *) &pasv_addr.sin_addr;
138836933Skarels p = (char *) &pasv_addr.sin_port;
138926044Sminshall
139026044Sminshall #define UC(b) (((int) b) & 0xff)
139126044Sminshall
139226044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
139326044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
139436620Srick return;
139536620Srick
139636620Srick pasv_error:
139736620Srick (void) close(pdata);
139836620Srick pdata = -1;
139936620Srick perror_reply(425, "Can't open passive connection");
140036620Srick return;
140126044Sminshall }
140226044Sminshall
140336304Skarels /*
140436304Skarels * Generate unique name for file with basename "local".
140536304Skarels * The file named "local" is already known to exist.
140636304Skarels * Generates failure reply on error.
140736304Skarels */
140855258Sandrew static char *
gunique(local)140926044Sminshall gunique(local)
141026044Sminshall char *local;
141126044Sminshall {
141226044Sminshall static char new[MAXPATHLEN];
141336304Skarels struct stat st;
141455258Sandrew int count;
141555258Sandrew char *cp;
141626044Sminshall
141760087Sbostic cp = strrchr(local, '/');
141836304Skarels if (cp)
141926044Sminshall *cp = '\0';
142036620Srick if (stat(cp ? local : ".", &st) < 0) {
142136933Skarels perror_reply(553, cp ? local : ".");
142266711Spendry return ((char *) 0);
142326044Sminshall }
142436620Srick if (cp)
142536620Srick *cp = '/';
142626044Sminshall (void) strcpy(new, local);
142726044Sminshall cp = new + strlen(new);
142826044Sminshall *cp++ = '.';
142936304Skarels for (count = 1; count < 100; count++) {
143055258Sandrew (void)sprintf(cp, "%d", count);
143136304Skarels if (stat(new, &st) < 0)
143266711Spendry return (new);
143326044Sminshall }
143436304Skarels reply(452, "Unique file name cannot be created.");
143566711Spendry return (NULL);
143626044Sminshall }
143736304Skarels
143836304Skarels /*
143936304Skarels * Format and send reply containing system error number.
144036304Skarels */
144155258Sandrew void
perror_reply(code,string)144236304Skarels perror_reply(code, string)
144336304Skarels int code;
144436304Skarels char *string;
144536304Skarels {
144666711Spendry
144742412Sbostic reply(code, "%s: %s.", string, strerror(errno));
144836304Skarels }
144936620Srick
145036620Srick static char *onefile[] = {
145136620Srick "",
145236620Srick 0
145336620Srick };
145436620Srick
145555258Sandrew void
send_file_list(whichf)145666711Spendry send_file_list(whichf)
145766711Spendry char *whichf;
145836620Srick {
145936620Srick struct stat st;
146036620Srick DIR *dirp = NULL;
146146669Sbostic struct dirent *dir;
146236620Srick FILE *dout = NULL;
146366711Spendry char **dirlist, *dirname;
146437459Skarels int simple = 0;
146566711Spendry int freeglob = 0;
146666711Spendry glob_t gl;
146736620Srick
146866711Spendry if (strpbrk(whichf, "~{[*?") != NULL) {
146966726Spendry int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
147036933Skarels
147166711Spendry memset(&gl, 0, sizeof(gl));
147266711Spendry freeglob = 1;
147366726Spendry if (glob(whichf, flags, 0, &gl)) {
147466711Spendry reply(550, "not found");
147566711Spendry goto out;
147666711Spendry } else if (gl.gl_pathc == 0) {
147736620Srick errno = ENOENT;
147866711Spendry perror_reply(550, whichf);
147966711Spendry goto out;
148036620Srick }
148166711Spendry dirlist = gl.gl_pathv;
148236620Srick } else {
148366711Spendry onefile[0] = whichf;
148436620Srick dirlist = onefile;
148537459Skarels simple = 1;
148636620Srick }
148736933Skarels
148836933Skarels if (setjmp(urgcatch)) {
148936933Skarels transflag = 0;
149066711Spendry goto out;
149136933Skarels }
149236620Srick while (dirname = *dirlist++) {
149336620Srick if (stat(dirname, &st) < 0) {
149436933Skarels /*
149536933Skarels * If user typed "ls -l", etc, and the client
149636933Skarels * used NLST, do what the user meant.
149736933Skarels */
149836933Skarels if (dirname[0] == '-' && *dirlist == NULL &&
149936933Skarels transflag == 0) {
150036933Skarels retrieve("/bin/ls %s", dirname);
150166711Spendry goto out;
150236933Skarels }
150366711Spendry perror_reply(550, whichf);
150436620Srick if (dout != NULL) {
150536620Srick (void) fclose(dout);
150636933Skarels transflag = 0;
150736620Srick data = -1;
150836620Srick pdata = -1;
150936620Srick }
151066711Spendry goto out;
151136620Srick }
151236933Skarels
151366711Spendry if (S_ISREG(st.st_mode)) {
151436620Srick if (dout == NULL) {
151537459Skarels dout = dataconn("file list", (off_t)-1, "w");
151636620Srick if (dout == NULL)
151766711Spendry goto out;
151836933Skarels transflag++;
151936620Srick }
152038158Srick fprintf(dout, "%s%s\n", dirname,
152138158Srick type == TYPE_A ? "\r" : "");
152236933Skarels byte_count += strlen(dirname) + 1;
152336620Srick continue;
152466711Spendry } else if (!S_ISDIR(st.st_mode))
152536620Srick continue;
152636620Srick
152736620Srick if ((dirp = opendir(dirname)) == NULL)
152836620Srick continue;
152936620Srick
153036620Srick while ((dir = readdir(dirp)) != NULL) {
153136933Skarels char nbuf[MAXPATHLEN];
153236620Srick
153336620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1)
153436620Srick continue;
153536933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
153636933Skarels dir->d_namlen == 2)
153736620Srick continue;
153836620Srick
153936933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name);
154036933Skarels
154136620Srick /*
154236933Skarels * We have to do a stat to insure it's
154336933Skarels * not a directory or special file.
154436620Srick */
154537459Skarels if (simple || (stat(nbuf, &st) == 0 &&
154666711Spendry S_ISREG(st.st_mode))) {
154736620Srick if (dout == NULL) {
154837459Skarels dout = dataconn("file list", (off_t)-1,
154936620Srick "w");
155036620Srick if (dout == NULL)
155166711Spendry goto out;
155236933Skarels transflag++;
155336620Srick }
155436620Srick if (nbuf[0] == '.' && nbuf[1] == '/')
155538158Srick fprintf(dout, "%s%s\n", &nbuf[2],
155638158Srick type == TYPE_A ? "\r" : "");
155736620Srick else
155838158Srick fprintf(dout, "%s%s\n", nbuf,
155938158Srick type == TYPE_A ? "\r" : "");
156036933Skarels byte_count += strlen(nbuf) + 1;
156136620Srick }
156236620Srick }
156336620Srick (void) closedir(dirp);
156436620Srick }
156536620Srick
156636933Skarels if (dout == NULL)
156736933Skarels reply(550, "No files found.");
156836933Skarels else if (ferror(dout) != 0)
156936933Skarels perror_reply(550, "Data connection");
157036933Skarels else
157136620Srick reply(226, "Transfer complete.");
157236620Srick
157336933Skarels transflag = 0;
157436933Skarels if (dout != NULL)
157536620Srick (void) fclose(dout);
157636620Srick data = -1;
157736620Srick pdata = -1;
157866711Spendry out:
157966711Spendry if (freeglob) {
158066711Spendry freeglob = 0;
158166711Spendry globfree(&gl);
158266711Spendry }
158336620Srick }
158436620Srick
158536620Srick #ifdef SETPROCTITLE
158636620Srick /*
158755258Sandrew * Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
158855258Sandrew * Warning, since this is usually started from inetd.conf, it often doesn't
158955258Sandrew * have much of an environment or arglist to overwrite.
159036620Srick */
159155258Sandrew void
159255258Sandrew #if __STDC__
setproctitle(const char * fmt,...)159355258Sandrew setproctitle(const char *fmt, ...)
159455258Sandrew #else
159555258Sandrew setproctitle(fmt, va_alist)
159655258Sandrew char *fmt;
159755258Sandrew va_dcl
159855258Sandrew #endif
159936620Srick {
160066711Spendry int i;
160155258Sandrew va_list ap;
160266711Spendry char *p, *bp, ch;
160366711Spendry char buf[LINE_MAX];
160466711Spendry
160555258Sandrew #if __STDC__
160655258Sandrew va_start(ap, fmt);
160755258Sandrew #else
160855258Sandrew va_start(ap);
160955258Sandrew #endif
161055258Sandrew (void)vsnprintf(buf, sizeof(buf), fmt, ap);
161136620Srick
161236620Srick /* make ps print our process name */
161336620Srick p = Argv[0];
161436620Srick *p++ = '-';
161536620Srick
161636620Srick i = strlen(buf);
161736620Srick if (i > LastArgv - p - 2) {
161836620Srick i = LastArgv - p - 2;
161936620Srick buf[i] = '\0';
162036620Srick }
162136620Srick bp = buf;
162236620Srick while (ch = *bp++)
162336620Srick if (ch != '\n' && ch != '\r')
162436620Srick *p++ = ch;
162536620Srick while (p < LastArgv)
162636620Srick *p++ = ' ';
162736620Srick }
162836620Srick #endif /* SETPROCTITLE */
1629