xref: /NextBSD/contrib/opie/opieftpd.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /* opieftpd.c: Main program for an FTP daemon.
2 
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9 
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14 
15 	History:
16 
17 	Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18 		opiestrncpy(). Fix incorrect use of setproctitle().
19 	Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20 		done already (and conditionally) in opie_cfg.h.
21 	Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22 		Merged in a security fix to BSD-derived ftpds.
23 	Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24 		Moved LS_COMMAND here.
25 	Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26                 Removed useless strings (I don't think that removing the
27                 ucb copyright one is a problem -- please let me know if
28                 I'm wrong). Changed default CMASK to 077. Removed random
29                 comments. Use ANSI stdargs for reply/lreply if we can,
30                 added stdargs version of reply/lreply. Don't declare the
31                 tos variable unless IP_TOS defined. Include stdargs headers
32                 early. More headers ifdefed. Made everything static.
33                 Got rid of gethostname() call and use of hostname. Pared
34                 down status response for places where header files frequently
35                 cause trouble. Made logging of user logins (ala -l)
36                 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37 	Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38 	        result of opiechallenge (allows access control to work).
39 		Added patches for AIX. Symbol changes for autoconf.
40         Modified at NRL for OPIE 2.01. Changed password lookup handling
41                 to avoid problems with drain-bamaged shadow password packages.
42                 Properly handle internal state for anonymous FTP. Unlock
43                 user accounts properly if login fails because of /etc/shells.
44                 Make sure to close syslog by function to avoid problems with
45                 drain bamaged syslog implementations.
46 	Modified at NRL for OPIE 2.0.
47 	Originally from BSD Net/2.
48 
49 	        There is some really, really ugly code in here.
50 
51 $FreeBSD$
52 */
53 /*
54  * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55  * All rights reserved.
56  *
57  * Redistribution and use in source and binary forms, with or without
58  * modification, are permitted provided that the following conditions
59  * are met:
60  * 1. Redistributions of source code must retain the above copyright
61  *    notice, this list of conditions and the following disclaimer.
62  * 2. Redistributions in binary form must reproduce the above copyright
63  *    notice, this list of conditions and the following disclaimer in the
64  *    documentation and/or other materials provided with the distribution.
65  * 3. All advertising materials mentioning features or use of this software
66  *    must display the following acknowledgement:
67  *      This product includes software developed by the University of
68  *      California, Berkeley and its contributors.
69  * 4. Neither the name of the University nor the names of its contributors
70  *    may be used to endorse or promote products derived from this software
71  *    without specific prior written permission.
72  *
73  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83  * SUCH DAMAGE.
84  */
85 
86 #include "opie_cfg.h"
87 
88 #if HAVE_ANSISTDARG
89 #include <stdarg.h>
90 #endif /* HAVE_ANSISTDARG */
91 
92 /*
93  * FTP server.
94  */
95 
96 #if HAVE_SYS_PARAM_H
97 #include <sys/param.h>
98 #endif /* HAVE_SYS_PARAM_H */
99 #include <sys/stat.h>
100 /* #include <sys/ioctl.h> */
101 #include <sys/socket.h>
102 #include <sys/wait.h>
103 #ifdef SYS_FCNTL_H
104 #include <sys/fcntl.h>
105 #else
106 #include <fcntl.h>
107 #endif	/* SYS_FCNTL_H */
108 #include <sys/types.h>
109 
110 #include <netinet/in.h>
111 #include <netinet/in_systm.h>
112 #include <netinet/ip.h>
113 
114 #define	FTP_NAMES
115 #include <arpa/ftp.h>
116 #include <arpa/inet.h>
117 #include <arpa/telnet.h>
118 
119 #include <signal.h>
120 #include <fcntl.h>
121 #if HAVE_TIME_H
122 #include <time.h>
123 #endif /* HAVE_TIME_H */
124 #if HAVE_PWD_H
125 #include <pwd.h>
126 #endif /* HAVE_PWD_H */
127 #include <setjmp.h>
128 #include <netdb.h>
129 #include <errno.h>
130 #include <syslog.h>
131 #if HAVE_UNISTD_H
132 #include <unistd.h>
133 #endif /* HAVE_UNISTD_H */
134 #include <stdio.h>
135 #include <ctype.h>
136 #include <stdlib.h>
137 #include <string.h>
138 #include <grp.h>
139 
140 #include "opie.h"
141 
142 #if HAVE_SHADOW_H
143 #include <shadow.h>
144 #endif /* HAVE_SHADOW_H */
145 
146 #if HAVE_CRYPT_H
147 #include <crypt.h>
148 #endif /* HAVE_CRYPT_H */
149 
150 #if HAVE_SYS_UTSNAME_H
151 #include <sys/utsname.h>
152 #endif /* HAVE_SYS_UTSNAME_H */
153 
154 #ifdef _AIX
155 #include <sys/id.h>
156 #include <sys/priv.h>
157 #endif /* _AIX */
158 
159 #ifdef IP_TOS
160 #ifndef IPTOS_THROUGHPUT
161 #undef IP_TOS
162 #endif /* !IPTOS_THROUGHPUT */
163 #ifndef IPTOS_LOWDELAY
164 #undef IP_TOS
165 #endif /* !IPTOS_LOWDELAY */
166 #endif /* IP_TOS */
167 
168 extern int errno;
169 extern char *home;	/* pointer to home directory for glob */
170 extern FILE *ftpd_popen __P((char *, char *));
171 extern int ftpd_pclose __P((FILE *));
172 extern char cbuf[];
173 extern off_t restart_point;
174 
175 static struct sockaddr_in ctrl_addr;
176 static struct sockaddr_in data_source;
177 struct sockaddr_in data_dest;
178 struct sockaddr_in his_addr;
179 static struct sockaddr_in pasv_addr;
180 
181 static int data;
182 jmp_buf errcatch;
183 static jmp_buf urgcatch;
184 int logged_in;
185 struct passwd *pw;
186 int debug;
187 int timeout = 900;	/* timeout after 15 minutes of inactivity */
188 int maxtimeout = 7200;	/* don't allow idle time to be set beyond 2 hours */
189 
190 #if DOANONYMOUS
191 static int guest;
192 #endif	/* DOANONYMOUS */
193 int type;
194 int form;
195 static int stru;	/* avoid C keyword */
196 static int mode;
197 int usedefault = 1;	/* for data transfers */
198 int pdata = -1;	/* for passive mode */
199 static int transflag;
200 static off_t file_size;
201 static off_t byte_count;
202 
203 #if (!defined(CMASK) || CMASK == 0)
204 #undef CMASK
205 #define CMASK 077
206 #endif
207 
208 static int defumask = CMASK;	/* default umask value */
209 char tmpline[7];
210 char remotehost[MAXHOSTNAMELEN];
211 
212 /*
213  * Timeout intervals for retrying connections
214  * to hosts that don't accept PORT cmds.  This
215  * is a kludge, but given the problems with TCP...
216  */
217 #define	SWAITMAX	90	/* wait at most 90 seconds */
218 #define	SWAITINT	5	/* interval between retries */
219 
220 static int swaitmax = SWAITMAX;
221 static int swaitint = SWAITINT;
222 
223 #if DOTITLE
224 static char **Argv = NULL;	/* pointer to argument vector */
225 static char *LastArgv = NULL;	/* end of argv */
226 static char proctitle[BUFSIZ];	/* initial part of title */
227 #endif	/* DOTITLE */
228 
229 static int af_pwok = 0, pwok = 0;
230 static struct opie opiestate;
231 
232 VOIDRET perror_reply __P((int, char *));
233 VOIDRET dologout __P((int));
234 char *getline __P((char *, int, FILE *));
235 VOIDRET upper __P((char *));
236 
237 static VOIDRET lostconn __P((int));
238 static VOIDRET myoob __P((int));
239 static FILE *getdatasock __P((char *));
240 static FILE *dataconn __P((char *, off_t, char *));
241 static int checkuser __P((char *));
242 static VOIDRET end_login __P((void));
243 static VOIDRET send_data __P((FILE *, FILE *, off_t));
244 static int receive_data __P((FILE *, FILE *));
245 static char *gunique __P((char *));
246 static char *sgetsave __P((char *));
247 
248 int opielogwtmp __P((char *, char *, char *, char *));
249 
250 int fclose __P((FILE *));
251 
252 #ifdef HAVE_ANSISTDARG
253 VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
254 {
255   va_list ap;
256   char buffer[1024];
257 
258   va_start(ap, fmt);
259   vsprintf(buffer, fmt, ap);
260   va_end(ap);
261 
262   printf("%d %s\r\n", n, buffer);
263   fflush(stdout);
264   if (debug)
265     syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
266 }
267 #else /* HAVE_ANSISTDARG */
268 VOIDRET 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)
269 {
270   printf("%d ", n);
271   printf(fmt, p0, p1, p2, p3, p4, p5);
272   printf("\r\n");
273   fflush(stdout);
274   if (debug) {
275     syslog(LOG_DEBUG, "<--- %d ", n);
276     syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
277   }
278 }
279 #endif /* HAVE_ANSISTDARG */
280 
281 #ifdef HAVE_ANSISTDARG
282 VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
283 {
284   va_list ap;
285   char buffer[1024];
286 
287   va_start(ap, fmt);
288   vsprintf(buffer, fmt, ap);
289   va_end(ap);
290 
291   printf("%d- %s\r\n", n, buffer);
292   fflush(stdout);
293   if (debug)
294     syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
295 }
296 #else /* HAVE_ANSISTDARG */
297 VOIDRET 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)
298 {
299   printf("%d- ", n);
300   printf(fmt, p0, p1, p2, p3, p4, p5);
301   printf("\r\n");
302   fflush(stdout);
303   if (debug) {
304     syslog(LOG_DEBUG, "<--- %d- ", n);
305     syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
306   }
307 }
308 #endif /* HAVE_ANSISTDARG */
309 
310 VOIDRET enable_signalling FUNCTION_NOARGS
311 {
312 	signal(SIGPIPE, lostconn);
313 	if ((int)signal(SIGURG, myoob) < 0)
314 		syslog(LOG_ERR, "signal: %m");
315 }
316 
317 VOIDRET disable_signalling FUNCTION_NOARGS
318 {
319 	signal(SIGPIPE, SIG_IGN);
320 	if ((int)signal(SIGURG, SIG_IGN) < 0)
321 		syslog(LOG_ERR, "signal: %m");
322 }
323 
324 static VOIDRET lostconn FUNCTION((input), int input)
325 {
326   if (debug)
327     syslog(LOG_DEBUG, "lost connection");
328   dologout(-1);
329 }
330 
331 static char ttyline[20];
332 
333 /*
334  * Helper function for sgetpwnam().
335  */
336 static char *sgetsave FUNCTION((s), char *s)
337 {
338   char *new = malloc((unsigned) strlen(s) + 1);
339 
340   if (new == NULL) {
341     perror_reply(421, "Local resource failure: malloc");
342     dologout(1);
343     /* NOTREACHED */
344   }
345   strcpy(new, s);
346   return (new);
347 }
348 
349 /*
350  * Save the result of a getpwnam.  Used for USER command, since
351  * the data returned must not be clobbered by any other command
352  * (e.g., globbing).
353  */
354 static struct passwd *sgetpwnam FUNCTION((name), char *name)
355 {
356   static struct passwd save;
357   register struct passwd *p;
358 
359 #if HAVE_SHADOW
360   struct spwd *spwd;
361 #endif /* HAVE_SHADOW */
362 
363   if ((p = getpwnam(name)) == NULL)
364     return (p);
365 
366 #if HAVE_SHADOW
367   if ((spwd = getspnam(name)) == NULL)
368     return NULL;
369 
370   endspent();
371 
372   p->pw_passwd = spwd->sp_pwdp;
373 #endif /* HAVE_SHADOW */
374 
375   endpwent();
376 
377   if (save.pw_name) {
378     free(save.pw_name);
379     free(save.pw_passwd);
380     free(save.pw_gecos);
381     free(save.pw_dir);
382     free(save.pw_shell);
383   }
384   save = *p;
385   save.pw_name = sgetsave(p->pw_name);
386   save.pw_passwd = sgetsave(p->pw_passwd);
387   save.pw_gecos = sgetsave(p->pw_gecos);
388   save.pw_dir = sgetsave(p->pw_dir);
389   save.pw_shell = sgetsave(p->pw_shell);
390   return (&save);
391 }
392 
393 int login_attempts;	/* number of failed login attempts */
394 int askpasswd;	/* had user command, ask for passwd */
395 
396 /*
397  * USER command.
398  * Sets global passwd pointer pw if named account exists and is acceptable;
399  * sets askpasswd if a PASS command is expected.  If logged in previously,
400  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
401  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
403  * requesting login privileges.  Disallow anyone who does not have a standard
404  * shell as returned by getusershell().  Disallow anyone mentioned in the file
405  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406  */
407 int user FUNCTION((name), char *name)
408 {
409   register char *cp;
410   char *shell;
411 
412   if (logged_in) {
413 #if DOANONYMOUS
414     if (guest) {
415       reply(530, "Can't change user from guest login.");
416       return -1;
417     }
418 #endif	/* DOANONMOUS */
419     end_login();
420   }
421   askpasswd = 1;
422 #if DOANONYMOUS
423   guest = 0;
424   if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
425     if (!checkuser("ftp") && !checkuser("anonymous"))
426       if ((pw = sgetpwnam("ftp")) != NULL) {
427 	guest = 1;
428 	askpasswd = 1;
429 	reply(331, "Guest login ok, send your e-mail address as your password.");
430 	syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
431         return 0;
432       }
433 #endif	/* DOANONYMOUS */
434   if (pw = sgetpwnam(name)) {
435     if ((shell = pw->pw_shell) == NULL || *shell == 0)
436       shell = _PATH_BSHELL;
437     while ((cp = getusershell()) != NULL)
438       if (!strcmp(cp, shell))
439 	break;
440     endusershell();
441     if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
442 #if DEBUG
443       if (!cp)
444         syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
445       if (checkuser(name))
446         syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
447       if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
448         syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
449 #endif /* DEBUG */
450       pw = (struct passwd *) NULL;
451       askpasswd = -1;
452     }
453   }
454   {
455     char prompt[OPIE_CHALLENGE_MAX + 1];
456 
457     opiechallenge(&opiestate, name, prompt);
458 
459     if (askpasswd == -1) {
460       syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
461       pwok = 0;
462     } else
463       pwok = af_pwok && opiealways(pw->pw_dir);
464 
465 #if NEW_PROMPTS
466     reply(331, "Response to %s %s for %s.", prompt,
467 #else /* NEW_PROMPTS */
468     reply(331, "OTP response %s %s for %s.", prompt,
469 #endif /* NEW_PROMPTS */
470 	  pwok ? "requested" : "required", name);
471   }
472   /* Delay before reading passwd after first failed attempt to slow down
473      passwd-guessing programs. */
474   if (login_attempts)
475     sleep((unsigned) login_attempts);
476 
477   return 0;
478 }
479 
480 /*
481  * Check if a user is in the file _PATH_FTPUSERS
482  */
483 static int checkuser FUNCTION((name), char *name)
484 {
485   register FILE *fd;
486   register char *p;
487   char line[BUFSIZ];
488 
489   if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
490     while (fgets(line, sizeof(line), fd) != NULL)
491       if ((p = strchr(line, '\n')) != NULL) {
492 	*p = '\0';
493 	if (line[0] == '#')
494 	  continue;
495 	if (!strcmp(line, name)) {
496           fclose(fd);
497 	  return (1);
498         }
499       }
500     fclose(fd);
501   }
502   return (0);
503 }
504 
505 /*
506  * Terminate login as previous user, if any, resetting state;
507  * used when USER command is given or login fails.
508  */
509 static VOIDRET end_login FUNCTION_NOARGS
510 {
511   disable_signalling();
512   if (seteuid((uid_t) 0))
513     syslog(LOG_ERR, "Can't set euid");
514   if (logged_in)
515     opielogwtmp(ttyline, "", "", "ftp");
516   pw = NULL;
517   logged_in = 0;
518 #if DOANONYMOUS
519   guest = 0;
520 #endif	/* DOANONYMOUS */
521   enable_signalling();
522 }
523 
524 VOIDRET pass FUNCTION((passwd), char *passwd)
525 {
526   int legit = askpasswd + 1, i;
527 
528   if (logged_in || askpasswd == 0) {
529     reply(503, "Login with USER first.");
530     return;
531   }
532   askpasswd = 0;
533 
534 #if DOANONYMOUS
535   if (!guest) { /* "ftp" is only account allowed no password */
536 #endif	/* DOANONYMOUS */
537     i = opieverify(&opiestate, passwd);
538     if (legit && i && pwok)
539       i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
540     if (!legit || i) {
541       reply(530, "Login incorrect.");
542       pw = NULL;
543       if (login_attempts++ >= 5) {
544 	syslog(LOG_WARNING,
545 	       "Repeated login failures for user %s from %s",
546 	       pw->pw_name, remotehost);
547 	exit(0);
548       }
549       return;
550     }
551 #if DOANONYMOUS
552   } else
553     if ((passwd[0] <= ' ') ||  checkuser(passwd)) {
554       reply(530, "No identity, no service.");
555       syslog(LOG_DEBUG, "Bogus address: %s", passwd);
556       exit(0);
557     }
558 #endif	/* DOANONYMOUS */
559   login_attempts = 0;	/* this time successful */
560   if (setegid((gid_t) pw->pw_gid) < 0) {
561     reply(550, "Can't set gid.");
562     syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
563     return;
564   }
565   initgroups(pw->pw_name, pw->pw_gid);
566 
567   /* open wtmp before chroot */
568   sprintf(ttyline, "ftp%d", getpid());
569   opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
570   logged_in = 1;
571 
572 #if DOANONYMOUS
573   if (guest) {
574     /* We MUST do a chdir() after the chroot. Otherwise the old current
575        directory will be accessible as "." outside the new root! */
576     if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
577       reply(550, "Can't set guest privileges.");
578       goto bad;
579     }
580   } else
581 #endif	/* DOANONYMOUS */
582     if (chdir(pw->pw_dir) < 0) {
583       if (chdir("/") < 0) {
584 	reply(530, "User %s: can't change directory to %s.",
585 	      pw->pw_name, pw->pw_dir);
586 	goto bad;
587       } else
588 	lreply(230, "No directory! Logging in with home=/");
589     }
590 /* This patch was contributed by an OPIE user. We don't know what it
591    does, exactly. It may or may not work. */
592 #ifdef _AIX
593    {
594        priv_t priv;
595        priv.pv_priv[0] = 0;
596        priv.pv_priv[1] = 0;
597        setgroups(NULL, NULL);
598        if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
599                    &priv, sizeof(priv_t)) < 0 ||
600 	   setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
601            setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
602            seteuid((uid_t)pw->pw_uid) < 0) {
603                reply(550, "Can't set uid (_AIX3).");
604                goto bad;
605        }
606     }
607 #else /* _AIX */
608   if (seteuid((uid_t) pw->pw_uid) < 0) {
609     reply(550, "Can't set uid.");
610     goto bad;
611   }
612 #endif /* _AIX */
613  /*
614   * Display a login message, if it exists.
615   * N.B. reply(230,) must follow the message.
616   */
617   {
618   FILE *fd;
619 
620   if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
621     char *cp, line[128];
622 
623     while (fgets(line, sizeof(line), fd) != NULL) {
624       if ((cp = strchr(line, '\n')) != NULL)
625         *cp = '\0';
626       lreply(230, "%s", line);
627     }
628     (void) fflush(stdout);
629     (void) fclose(fd);
630   }
631   }
632 #if DOANONYMOUS
633   if (guest) {
634     reply(230, "Guest login ok, access restrictions apply.");
635 #if DOTITLE
636     setproctitle("%s: anonymous/%.*s", remotehost,
637             sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
638 	    passwd);
639 #endif /* DOTITLE */
640     syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
641             remotehost, passwd);
642   } else
643 #endif	/* DOANONYMOUS */
644   {
645     reply(230, "User %s logged in.", pw->pw_name);
646 
647 #if DOTITLE
648     setproctitle("%s: %s", remotehost, pw->pw_name);
649 #endif /* DOTITLE */
650     syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
651   }
652   home = pw->pw_dir;	/* home dir for globbing */
653   umask(defumask);
654   return;
655 
656 bad:
657   /* Forget all about it... */
658   end_login();
659 }
660 
661 VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
662 {
663   FILE *fin, *dout;
664   struct stat st;
665   int (*closefunc) ();
666 
667   if (cmd == 0) {
668     fin = fopen(name, "r"), closefunc = fclose;
669     st.st_size = 0;
670   } else {
671     char line[BUFSIZ];
672 
673     snprintf(line, sizeof(line), cmd, name);
674     name = line;
675     fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
676     st.st_size = -1;
677 #if HAVE_ST_BLKSIZE
678     st.st_blksize = BUFSIZ;
679 #endif /* HAVE_ST_BLKSIZE */
680   }
681   if (fin == NULL) {
682     if (errno != 0)
683       perror_reply(550, name);
684     return;
685   }
686   if (cmd == 0 &&
687       (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
688     reply(550, "%s: not a plain file.", name);
689     goto done;
690   }
691   if (restart_point) {
692     if (type == TYPE_A) {
693       register int i, n, c;
694 
695       n = restart_point;
696       i = 0;
697       while (i++ < n) {
698 	if ((c = getc(fin)) == EOF) {
699 	  perror_reply(550, name);
700 	  goto done;
701 	}
702 	if (c == '\n')
703 	  i++;
704       }
705     } else
706       if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
707 	perror_reply(550, name);
708 	goto done;
709       }
710   }
711   dout = dataconn(name, st.st_size, "w");
712   if (dout == NULL)
713     goto done;
714 #if HAVE_ST_BLKSIZE
715   send_data(fin, dout, st.st_blksize);
716 #else /* HAVE_ST_BLKSIZE */
717   send_data(fin, dout, BUFSIZ);
718 #endif /* HAVE_ST_BLKSIZE */
719   fclose(dout);
720   data = -1;
721   pdata = -1;
722 done:
723   (*closefunc) (fin);
724 }
725 
726 VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
727 {
728   FILE *fout, *din;
729   struct stat st;
730   int (*closefunc) ();
731 
732   if (unique && stat(name, &st) == 0 &&
733       (name = gunique(name)) == NULL)
734     return;
735 
736   if (restart_point)
737     mode = "r+w";
738   fout = fopen(name, mode);
739   closefunc = fclose;
740   if (fout == NULL) {
741     perror_reply(553, name);
742     return;
743   }
744   if (restart_point) {
745     if (type == TYPE_A) {
746       register int i, n, c;
747 
748       n = restart_point;
749       i = 0;
750       while (i++ < n) {
751 	if ((c = getc(fout)) == EOF) {
752 	  perror_reply(550, name);
753 	  goto done;
754 	}
755 	if (c == '\n')
756 	  i++;
757       }
758       /* We must do this seek to "current" position because we are changing
759          from reading to writing. */
760       if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
761 	perror_reply(550, name);
762 	goto done;
763       }
764     } else
765       if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
766 	perror_reply(550, name);
767 	goto done;
768       }
769   }
770   din = dataconn(name, (off_t) - 1, "r");
771   if (din == NULL)
772     goto done;
773   if (receive_data(din, fout) == 0) {
774     if (unique)
775       reply(226, "Transfer complete (unique file name:%s).",
776 	    name);
777     else
778       reply(226, "Transfer complete.");
779   }
780   fclose(din);
781   data = -1;
782   pdata = -1;
783 done:
784   (*closefunc) (fout);
785 }
786 
787 static FILE *getdatasock FUNCTION((mode), char *mode)
788 {
789   int s, on = 1, tries;
790 
791   if (data >= 0)
792     return (fdopen(data, mode));
793   disable_signalling();
794   if (seteuid((uid_t) 0))
795     syslog(LOG_ERR, "Can't set euid");
796   s = socket(AF_INET, SOCK_STREAM, 0);
797   if (s < 0)
798     goto bad;
799   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
800 		 (char *) &on, sizeof(on)) < 0)
801     goto bad;
802   /* anchor socket to avoid multi-homing problems */
803   data_source.sin_family = AF_INET;
804   data_source.sin_addr = ctrl_addr.sin_addr;
805   for (tries = 1;; tries++) {
806     if (bind(s, (struct sockaddr *) & data_source,
807 	     sizeof(data_source)) >= 0)
808       break;
809     if (errno != EADDRINUSE || tries > 10)
810       goto bad;
811     sleep(tries);
812   }
813   if (seteuid((uid_t) pw->pw_uid))
814     syslog(LOG_ERR, "Can't set euid");
815   enable_signalling();
816 #ifdef IP_TOS
817   on = IPTOS_THROUGHPUT;
818   if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
819     syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
820 #endif
821   return (fdopen(s, mode));
822 bad:
823   {
824   int t = errno;
825 
826   if (seteuid((uid_t) pw->pw_uid))
827     syslog(LOG_ERR, "Can't set euid");
828   enable_signalling();
829   close(s);
830 
831   errno = t;
832   }
833   return (NULL);
834 }
835 
836 static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
837 {
838   char sizebuf[32];
839   FILE *file;
840   int retry = 0;
841 #ifdef IP_TOS
842   int tos;
843 #endif /* IP_TOS */
844 
845   file_size = size;
846   byte_count = 0;
847   if (size != (off_t) - 1)
848     snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
849   else
850     strcpy(sizebuf, "");
851   if (pdata >= 0) {
852     struct sockaddr_in from;
853     int s, fromlen = sizeof(from);
854 
855     s = accept(pdata, (struct sockaddr *) & from, &fromlen);
856     if (s < 0) {
857       reply(425, "Can't open data connection.");
858       close(pdata);
859       pdata = -1;
860       return (NULL);
861     }
862     close(pdata);
863     pdata = s;
864 #ifdef IP_TOS
865     tos = IPTOS_LOWDELAY;
866     setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
867 		      sizeof(int));
868 
869 #endif
870     reply(150, "Opening %s mode data connection for %s%s.",
871 	  type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
872     return (fdopen(pdata, mode));
873   }
874   if (data >= 0) {
875     reply(125, "Using existing data connection for %s%s.",
876 	  name, sizebuf);
877     usedefault = 1;
878     return (fdopen(data, mode));
879   }
880   if (usedefault)
881     data_dest = his_addr;
882   usedefault = 1;
883   file = getdatasock(mode);
884   if (file == NULL) {
885     reply(425, "Can't create data socket (%s,%d): %s.",
886 	  inet_ntoa(data_source.sin_addr),
887 	  ntohs(data_source.sin_port), strerror(errno));
888     return (NULL);
889   }
890   data = fileno(file);
891   while (connect(data, (struct sockaddr *) & data_dest,
892 		 sizeof(data_dest)) < 0) {
893     if (errno == EADDRINUSE && retry < swaitmax) {
894       sleep((unsigned) swaitint);
895       retry += swaitint;
896       continue;
897     }
898     perror_reply(425, "Can't build data connection");
899     fclose(file);
900     data = -1;
901     return (NULL);
902   }
903   reply(150, "Opening %s mode data connection for %s%s.",
904 	type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
905   return (file);
906 }
907 
908 /*
909  * Tranfer the contents of "instr" to
910  * "outstr" peer using the appropriate
911  * encapsulation of the data subject
912  * to Mode, Structure, and Type.
913  *
914  * NB: Form isn't handled.
915  */
916 static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
917 {
918   register int c, cnt;
919   register char *buf;
920   int netfd, filefd;
921 
922   transflag++;
923   if (setjmp(urgcatch)) {
924     transflag = 0;
925     return;
926   }
927   switch (type) {
928 
929   case TYPE_A:
930     while ((c = getc(instr)) != EOF) {
931       byte_count++;
932       if (c == '\n') {
933 	if (ferror(outstr))
934 	  goto data_err;
935 	putc('\r', outstr);
936       }
937       putc(c, outstr);
938     }
939     fflush(outstr);
940     transflag = 0;
941     if (ferror(instr))
942       goto file_err;
943     if (ferror(outstr))
944       goto data_err;
945     reply(226, "Transfer complete.");
946     return;
947 
948   case TYPE_I:
949   case TYPE_L:
950     if ((buf = malloc((u_int) blksize)) == NULL) {
951       transflag = 0;
952       perror_reply(451, "Local resource failure: malloc");
953       return;
954     }
955     netfd = fileno(outstr);
956     filefd = fileno(instr);
957     while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
958 	   write(netfd, buf, cnt) == cnt)
959       byte_count += cnt;
960     transflag = 0;
961     free(buf);
962     if (cnt != 0) {
963       if (cnt < 0)
964 	goto file_err;
965       goto data_err;
966     }
967     reply(226, "Transfer complete.");
968     return;
969   default:
970     transflag = 0;
971     reply(550, "Unimplemented TYPE %d in send_data", type);
972     return;
973   }
974 
975 data_err:
976   transflag = 0;
977   perror_reply(426, "Data connection");
978   return;
979 
980 file_err:
981   transflag = 0;
982   perror_reply(551, "Error on input file");
983 }
984 
985 /*
986  * Transfer data from peer to
987  * "outstr" using the appropriate
988  * encapulation of the data subject
989  * to Mode, Structure, and Type.
990  *
991  * N.B.: Form isn't handled.
992  */
993 static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
994 {
995   register int c;
996   int cnt, bare_lfs = 0;
997   char buf[BUFSIZ];
998 
999   transflag++;
1000   if (setjmp(urgcatch)) {
1001     transflag = 0;
1002     return (-1);
1003   }
1004   switch (type) {
1005 
1006   case TYPE_I:
1007   case TYPE_L:
1008     while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1009       if (write(fileno(outstr), buf, cnt) != cnt)
1010 	goto file_err;
1011       byte_count += cnt;
1012     }
1013     if (cnt < 0)
1014       goto data_err;
1015     transflag = 0;
1016     return (0);
1017 
1018   case TYPE_E:
1019     reply(553, "TYPE E not implemented.");
1020     transflag = 0;
1021     return (-1);
1022 
1023   case TYPE_A:
1024     while ((c = getc(instr)) != EOF) {
1025       byte_count++;
1026       if (c == '\n')
1027 	bare_lfs++;
1028       while (c == '\r') {
1029 	if (ferror(outstr))
1030 	  goto data_err;
1031 	if ((c = getc(instr)) != '\n') {
1032 	  putc('\r', outstr);
1033 	  if (c == '\0' || c == EOF)
1034 	    goto contin2;
1035 	}
1036       }
1037       putc(c, outstr);
1038   contin2:;
1039     }
1040     fflush(outstr);
1041     if (ferror(instr))
1042       goto data_err;
1043     if (ferror(outstr))
1044       goto file_err;
1045     transflag = 0;
1046     if (bare_lfs) {
1047       lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1048       printf("   File may not have transferred correctly.\r\n");
1049     }
1050     return (0);
1051   default:
1052     reply(550, "Unimplemented TYPE %d in receive_data", type);
1053     transflag = 0;
1054     return (-1);
1055   }
1056 
1057 data_err:
1058   transflag = 0;
1059   perror_reply(426, "Data Connection");
1060   return (-1);
1061 
1062 file_err:
1063   transflag = 0;
1064   perror_reply(452, "Error writing file");
1065   return (-1);
1066 }
1067 
1068 VOIDRET statfilecmd FUNCTION((filename), char *filename)
1069 {
1070   char line[BUFSIZ];
1071   FILE *fin;
1072   int c;
1073 
1074 #if HAVE_LS_G_FLAG
1075   snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1076 #else /* HAVE_LS_G_FLAG */
1077   snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1078 #endif /* HAVE_LS_G_FLAG */
1079   fin = ftpd_popen(line, "r");
1080   lreply(211, "status of %s:", filename);
1081   while ((c = getc(fin)) != EOF) {
1082     if (c == '\n') {
1083       if (ferror(stdout)) {
1084 	perror_reply(421, "control connection");
1085 	ftpd_pclose(fin);
1086 	dologout(1);
1087 	/* NOTREACHED */
1088       }
1089       if (ferror(fin)) {
1090 	perror_reply(551, filename);
1091 	ftpd_pclose(fin);
1092 	return;
1093       }
1094       putc('\r', stdout);
1095     }
1096     putc(c, stdout);
1097   }
1098   ftpd_pclose(fin);
1099   reply(211, "End of Status");
1100 }
1101 
1102 VOIDRET statcmd FUNCTION_NOARGS
1103 {
1104 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1105   struct sockaddr_in *sin;
1106   u_char *a, *p;
1107 
1108   lreply(211, "FTP server status:");
1109   printf("     \r\n");
1110   printf("     Connected to %s", remotehost);
1111   if (!isdigit(remotehost[0]))
1112     printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1113   printf("\r\n");
1114   if (logged_in) {
1115 #if DOANONYMOUS
1116     if (guest)
1117       printf("     Logged in anonymously\r\n");
1118     else
1119 #endif	/* DOANONYMOUS */
1120       printf("     Logged in as %s\r\n", pw->pw_name);
1121   } else
1122     if (askpasswd)
1123       printf("     Waiting for password\r\n");
1124     else
1125       printf("     Waiting for user name\r\n");
1126   if (data != -1)
1127     printf("     Data connection open\r\n");
1128   else
1129     if (pdata != -1) {
1130       printf("     in Passive mode");
1131       sin = &pasv_addr;
1132       goto printaddr;
1133     } else
1134       if (usedefault == 0) {
1135 	printf("     PORT");
1136 	sin = &data_dest;
1137     printaddr:
1138 	a = (u_char *) & sin->sin_addr;
1139 	p = (u_char *) & sin->sin_port;
1140 #define UC(b) (((int) b) & 0xff)
1141 	printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1142 	       UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1143 #undef UC
1144       } else
1145 	printf("     No data connection\r\n");
1146   reply(211, "End of status");
1147 }
1148 
1149 VOIDRET opiefatal FUNCTION((s), char *s)
1150 {
1151   reply(451, "Error in server: %s\n", s);
1152   reply(221, "Closing connection due to server error.");
1153   dologout(0);
1154   /* NOTREACHED */
1155 }
1156 
1157 static VOIDRET ack FUNCTION((s), char *s)
1158 {
1159   reply(250, "%s command successful.", s);
1160 }
1161 
1162 VOIDRET nack FUNCTION((s), char *s)
1163 {
1164   reply(502, "%s command not implemented.", s);
1165 }
1166 
1167 VOIDRET yyerror FUNCTION((s), char *s)
1168 {
1169   char *cp;
1170 
1171   if (cp = strchr(cbuf, '\n'))
1172     *cp = '\0';
1173   reply(500, "'%s': command not understood.", cbuf);
1174 }
1175 
1176 VOIDRET delete FUNCTION((name), char *name)
1177 {
1178   struct stat st;
1179 
1180   if (stat(name, &st) < 0) {
1181     perror_reply(550, name);
1182     return;
1183   }
1184   if ((st.st_mode & S_IFMT) == S_IFDIR) {
1185     if (rmdir(name) < 0) {
1186       perror_reply(550, name);
1187       return;
1188     }
1189     goto done;
1190   }
1191   if (unlink(name) < 0) {
1192     perror_reply(550, name);
1193     return;
1194   }
1195 done:
1196   ack("DELE");
1197 }
1198 
1199 VOIDRET cwd FUNCTION((path), char *path)
1200 {
1201   if (chdir(path) < 0)
1202     perror_reply(550, path);
1203   else
1204     ack("CWD");
1205 }
1206 
1207 VOIDRET makedir FUNCTION((name), char *name)
1208 {
1209   if (mkdir(name, 0777) < 0)
1210     perror_reply(550, name);
1211   else
1212     reply(257, "MKD command successful.");
1213 }
1214 
1215 VOIDRET removedir FUNCTION((name), char *name)
1216 {
1217   if (rmdir(name) < 0)
1218     perror_reply(550, name);
1219   else
1220     ack("RMD");
1221 }
1222 
1223 VOIDRET pwd FUNCTION_NOARGS
1224 {
1225   char path[MAXPATHLEN + 1];
1226 
1227   if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1228     reply(550, "%s.", path);
1229   else
1230     reply(257, "\"%s\" is current directory.", path);
1231 }
1232 
1233 char *renamefrom FUNCTION((name), char *name)
1234 {
1235   struct stat st;
1236 
1237   if (stat(name, &st) < 0) {
1238     perror_reply(550, name);
1239     return ((char *) 0);
1240   }
1241   reply(350, "File exists, ready for destination name");
1242   return (name);
1243 }
1244 
1245 VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1246 {
1247   if (rename(from, to) < 0)
1248     perror_reply(550, "rename");
1249   else
1250     ack("RNTO");
1251 }
1252 
1253 static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1254 {
1255   struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1256 				     sizeof(struct in_addr), AF_INET);
1257   time_t t, time();
1258 
1259   if (hp)
1260     opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1261   else
1262     opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1263 #if DOTITLE
1264   setproctitle("%s: connected", remotehost);
1265 #endif	/* DOTITLE */
1266 
1267   t = time((time_t *) 0);
1268   syslog(LOG_INFO, "connection from %s at %s",
1269     remotehost, ctime(&t));
1270 }
1271 
1272 /*
1273  * Record logout in wtmp file
1274  * and exit with supplied status.
1275  */
1276 VOIDRET dologout FUNCTION((status), int status)
1277 {
1278   disable_signalling();
1279   if (logged_in) {
1280     if (seteuid((uid_t) 0))
1281       syslog(LOG_ERR, "Can't set euid");
1282     opielogwtmp(ttyline, "", "", "ftp");
1283   }
1284   /* beware of flushing buffers after a SIGPIPE */
1285   _exit(status);
1286 }
1287 
1288 static VOIDRET myoob FUNCTION((input), int input)
1289 {
1290   char *cp;
1291 
1292   /* only process if transfer occurring */
1293   if (!transflag)
1294     return;
1295   cp = tmpline;
1296   if (getline(cp, 7, stdin) == NULL) {
1297     reply(221, "You could at least say goodbye.");
1298     dologout(0);
1299   }
1300   upper(cp);
1301   if (strcmp(cp, "ABOR\r\n") == 0) {
1302     tmpline[0] = '\0';
1303     reply(426, "Transfer aborted. Data connection closed.");
1304     reply(226, "Abort successful");
1305     longjmp(urgcatch, 1);
1306   }
1307   if (strcmp(cp, "STAT\r\n") == 0) {
1308     if (file_size != (off_t) - 1)
1309       reply(213, "Status: %lu of %lu bytes transferred",
1310 	    byte_count, file_size);
1311     else
1312       reply(213, "Status: %lu bytes transferred", byte_count);
1313   }
1314 }
1315 
1316 /*
1317  * Note: a response of 425 is not mentioned as a possible response to
1318  *      the PASV command in RFC959. However, it has been blessed as
1319  *      a legitimate response by Jon Postel in a telephone conversation
1320  *      with Rick Adams on 25 Jan 89.
1321  */
1322 VOIDRET passive FUNCTION_NOARGS
1323 {
1324   int len;
1325   register char *p, *a;
1326 
1327   pdata = socket(AF_INET, SOCK_STREAM, 0);
1328   if (pdata < 0) {
1329     perror_reply(425, "Can't open passive connection");
1330     return;
1331   }
1332   pasv_addr = ctrl_addr;
1333   pasv_addr.sin_port = 0;
1334   if (seteuid((uid_t) 0))
1335     syslog(LOG_ERR, "Can't set euid");
1336   if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1337     seteuid((uid_t) pw->pw_uid);
1338     goto pasv_error;
1339   }
1340   if (seteuid((uid_t) pw->pw_uid))
1341     syslog(LOG_ERR, "Can't set euid");
1342   len = sizeof(pasv_addr);
1343   if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1344     goto pasv_error;
1345   if (listen(pdata, 1) < 0)
1346     goto pasv_error;
1347   a = (char *) &pasv_addr.sin_addr;
1348   p = (char *) &pasv_addr.sin_port;
1349 
1350 #define UC(b) (((int) b) & 0xff)
1351 
1352   reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1353 	UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1354   return;
1355 
1356 pasv_error:
1357   close(pdata);
1358   pdata = -1;
1359   perror_reply(425, "Can't open passive connection");
1360   return;
1361 }
1362 
1363 /*
1364  * Generate unique name for file with basename "local".
1365  * The file named "local" is already known to exist.
1366  * Generates failure reply on error.
1367  */
1368 static char *gunique FUNCTION((local), char *local)
1369 {
1370   static char new[MAXPATHLEN+1];
1371   struct stat st;
1372   char *cp = strrchr(local, '/');
1373   int count = 0;
1374 
1375   if (cp)
1376     *cp = '\0';
1377   if (stat(cp ? local : ".", &st) < 0) {
1378     perror_reply(553, cp ? local : ".");
1379     return ((char *) 0);
1380   }
1381   if (cp)
1382     *cp = '/';
1383   strcpy(new, local);
1384   cp = new + strlen(new);
1385   *cp++ = '.';
1386   for (count = 1; count < 100; count++) {
1387     snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1388     if (stat(new, &st) < 0)
1389       return (new);
1390   }
1391   reply(452, "Unique file name cannot be created.");
1392   return ((char *) 0);
1393 }
1394 
1395 /*
1396  * Format and send reply containing system error number.
1397  */
1398 VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1399 {
1400   reply(code, "%s: %s.", string, strerror(errno));
1401 }
1402 
1403 static char *onefile[] =
1404 {
1405   "",
1406   0
1407 };
1408 
1409 VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1410 {
1411   struct stat st;
1412   DIR *dirp = NULL;
1413   struct dirent *dir;
1414   FILE *dout = NULL;
1415   register char **dirlist, *dirname;
1416   int simple = 0;
1417 
1418   if (strpbrk(whichfiles, "~{[*?") != NULL) {
1419     extern char **ftpglob(), *globerr;
1420 
1421     globerr = NULL;
1422     dirlist = ftpglob(whichfiles);
1423     if (globerr != NULL) {
1424       reply(550, globerr);
1425       return;
1426     } else
1427       if (dirlist == NULL) {
1428 	errno = ENOENT;
1429 	perror_reply(550, whichfiles);
1430 	return;
1431       }
1432   } else {
1433     onefile[0] = whichfiles;
1434     dirlist = onefile;
1435     simple = 1;
1436   }
1437 
1438   if (setjmp(urgcatch)) {
1439     transflag = 0;
1440     return;
1441   }
1442   while (dirname = *dirlist++) {
1443     if (stat(dirname, &st) < 0) {
1444       /* If user typed "ls -l", etc, and the client used NLST, do what the
1445          user meant. */
1446       if (dirname[0] == '-' && *dirlist == NULL &&
1447 	  transflag == 0) {
1448 	retrieve("/bin/ls %s", dirname);
1449 	return;
1450       }
1451       perror_reply(550, whichfiles);
1452       if (dout != NULL) {
1453 	fclose(dout);
1454 	transflag = 0;
1455 	data = -1;
1456 	pdata = -1;
1457       }
1458       return;
1459     }
1460     if ((st.st_mode & S_IFMT) == S_IFREG) {
1461       if (dout == NULL) {
1462 	dout = dataconn("file list", (off_t) - 1, "w");
1463 	if (dout == NULL)
1464 	  return;
1465 	transflag++;
1466       }
1467       fprintf(dout, "%s%s\n", dirname,
1468 	      type == TYPE_A ? "\r" : "");
1469       byte_count += strlen(dirname) + 1;
1470       continue;
1471     } else
1472       if ((st.st_mode & S_IFMT) != S_IFDIR)
1473 	continue;
1474 
1475     if ((dirp = opendir(dirname)) == NULL)
1476       continue;
1477 
1478     while ((dir = readdir(dirp)) != NULL) {
1479       char nbuf[MAXPATHLEN+1];
1480 
1481       if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1482 	continue;
1483       if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1484 	  (strlen(dir->d_name) == 2))
1485 	continue;
1486 
1487       snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1488 
1489       /* We have to do a stat to insure it's not a directory or special file. */
1490       if (simple || (stat(nbuf, &st) == 0 &&
1491 		     (st.st_mode & S_IFMT) == S_IFREG)) {
1492 	if (dout == NULL) {
1493 	  dout = dataconn("file list", (off_t) - 1, "w");
1494 	  if (dout == NULL)
1495 	    return;
1496 	  transflag++;
1497 	}
1498 	if (nbuf[0] == '.' && nbuf[1] == '/')
1499 	  fprintf(dout, "%s%s\n", &nbuf[2],
1500 		  type == TYPE_A ? "\r" : "");
1501 	else
1502 	  fprintf(dout, "%s%s\n", nbuf,
1503 		  type == TYPE_A ? "\r" : "");
1504 	byte_count += strlen(nbuf) + 1;
1505       }
1506     }
1507     closedir(dirp);
1508   }
1509 
1510   if (dout == NULL)
1511     reply(550, "No files found.");
1512   else
1513     if (ferror(dout) != 0)
1514       perror_reply(550, "Data connection");
1515     else
1516       reply(226, "Transfer complete.");
1517 
1518   transflag = 0;
1519   if (dout != NULL)
1520     fclose(dout);
1521   data = -1;
1522   pdata = -1;
1523 }
1524 
1525 #if DOTITLE
1526 /*
1527  * clobber argv so ps will show what we're doing.
1528  * (stolen from sendmail)
1529  * warning, since this is usually started from inetd.conf, it
1530  * often doesn't have much of an environment or arglist to overwrite.
1531  */
1532 VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1533 {
1534   register char *p, *bp, ch;
1535   register int i;
1536   char buf[BUFSIZ];
1537 
1538   snprintf(buf, sizeof(buf), fmt, a, b, c);
1539 
1540   /* make ps print our process name */
1541   p = Argv[0];
1542   *p++ = '-';
1543 
1544   i = strlen(buf);
1545   if (i > LastArgv - p - 2) {
1546     i = LastArgv - p - 2;
1547     buf[i] = '\0';
1548   }
1549   bp = buf;
1550   while (ch = *bp++)
1551     if (ch != '\n' && ch != '\r')
1552       *p++ = ch;
1553   while (p < LastArgv)
1554     *p++ = ' ';
1555 }
1556 #endif	/* DOTITLE */
1557 
1558 VOIDRET catchexit FUNCTION_NOARGS
1559 {
1560   closelog();
1561 }
1562 
1563 int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1564 {
1565   int addrlen, on = 1;
1566   char *cp;
1567 #ifdef IP_TOS
1568   int tos;
1569 #endif /* IP_TOS */
1570 
1571   {
1572   int i;
1573 
1574   for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1575     close(i);
1576   }
1577 
1578   /* LOG_NDELAY sets up the logging connection immediately, necessary for
1579      anonymous ftp's that chroot and can't do it later. */
1580   openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1581   atexit(catchexit);
1582   addrlen = sizeof(his_addr);
1583   if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1584     syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1585     exit(1);
1586   }
1587   addrlen = sizeof(ctrl_addr);
1588   if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1589     syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1590     exit(1);
1591   }
1592 #ifdef IP_TOS
1593   tos = IPTOS_LOWDELAY;
1594   if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1595     syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1596 #endif
1597   data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1598   debug = 0;
1599 #if DOTITLE
1600   /* Save start and extent of argv for setproctitle. */
1601   Argv = argv;
1602   while (*envp)
1603     envp++;
1604   LastArgv = envp[-1] + strlen(envp[-1]);
1605 #endif	/* DOTITLE */
1606 
1607   argc--, argv++;
1608   while (argc > 0 && *argv[0] == '-') {
1609     for (cp = &argv[0][1]; *cp; cp++)
1610       switch (*cp) {
1611 
1612       case 'v':
1613 	debug = 1;
1614 	break;
1615 
1616       case 'd':
1617 	debug = 1;
1618 	break;
1619 
1620       case 'l':
1621 	break;
1622 
1623       case 't':
1624 	timeout = atoi(++cp);
1625 	if (maxtimeout < timeout)
1626 	  maxtimeout = timeout;
1627 	goto nextopt;
1628 
1629       case 'T':
1630 	maxtimeout = atoi(++cp);
1631 	if (timeout > maxtimeout)
1632 	  timeout = maxtimeout;
1633 	goto nextopt;
1634 
1635       case 'u':
1636 	{
1637 	  int val = 0;
1638 
1639 	  while (*++cp && *cp >= '0' && *cp <= '9')
1640 	    val = val * 8 + *cp - '0';
1641 	  if (*cp)
1642 	    fprintf(stderr, "ftpd: Bad value for -u\n");
1643 	  else
1644 	    defumask = val;
1645 	  goto nextopt;
1646 	}
1647 
1648       default:
1649 	fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1650 		*cp);
1651 	break;
1652       }
1653 nextopt:
1654     argc--, argv++;
1655   }
1656   freopen(_PATH_DEVNULL, "w", stderr);
1657   signal(SIGCHLD, SIG_IGN);
1658   enable_signalling();
1659 
1660   /* Try to handle urgent data inline */
1661 #ifdef SO_OOBINLINE
1662   if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1663     syslog(LOG_ERR, "setsockopt: %m");
1664 #endif
1665 
1666 #ifdef	F_SETOWN
1667   if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1668     syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1669 #endif
1670   dolog(&his_addr);
1671   /* Set up default state */
1672   data = -1;
1673   type = TYPE_A;
1674   form = FORM_N;
1675   stru = STRU_F;
1676   mode = MODE_S;
1677   tmpline[0] = '\0';
1678   af_pwok = opieaccessfile(remotehost);
1679 
1680   {
1681   FILE *fd;
1682   char line[128];
1683 
1684   /* If logins are disabled, print out the message. */
1685   if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1686     while (fgets(line, sizeof(line), fd) != NULL) {
1687       if ((cp = strchr(line, '\n')) != NULL)
1688         *cp = '\0';
1689       lreply(530, "%s", line);
1690     }
1691     (void) fflush(stdout);
1692     (void) fclose(fd);
1693     reply(530, "System not available.");
1694     exit(0);
1695   }
1696   if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1697     while (fgets(line, sizeof(line), fd) != NULL) {
1698       if ((cp = strchr(line, '\n')) != NULL)
1699         *cp = '\0';
1700       lreply(220, "%s", line);
1701     }
1702     (void) fflush(stdout);
1703     (void) fclose(fd);
1704     /* reply(220,) must follow */
1705   }
1706   };
1707 
1708   reply(220, "FTP server ready.");
1709 
1710   setjmp(errcatch);
1711   for (;;)
1712     yyparse();
1713   /* NOTREACHED */
1714   return 0;
1715 }
1716