The first a bug in pax and should be commited to FBSD, too.
[dragonfly.git] / contrib / lukemftpd / src / ftpd.c
1 /*      $NetBSD: ftpd.c,v 1.138 2002/02/11 11:45:07 lukem Exp $ */
2
3 /*
4  * Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Luke Mewburn.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 /*
40  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
41  *      The Regents of the University of California.  All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *      This product includes software developed by the University of
54  *      California, Berkeley and its contributors.
55  * 4. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71
72 /*
73  * Copyright (C) 1997 and 1998 WIDE Project.
74  * All rights reserved.
75  * 
76  * Redistribution and use in source and binary forms, with or without
77  * modification, are permitted provided that the following conditions
78  * are met:
79  * 1. Redistributions of source code must retain the above copyright
80  *    notice, this list of conditions and the following disclaimer.
81  * 2. Redistributions in binary form must reproduce the above copyright
82  *    notice, this list of conditions and the following disclaimer in the
83  *    documentation and/or other materials provided with the distribution.
84  * 3. Neither the name of the project nor the names of its contributors
85  *    may be used to endorse or promote products derived from this software
86  *    without specific prior written permission.
87  * 
88  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
89  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
92  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
93  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
95  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
96  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
97  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98  * SUCH DAMAGE.
99  */
100
101 /*
102  * FTP server.
103  */
104
105 #define FTP_NAMES
106
107 #include "lukemftpd.h"
108
109 #if HAVE_GETSPNAM
110 #include <shadow.h>
111 #endif
112
113 #include <arpa/telnet.h>
114
115 #ifdef SKEY
116 #include <skey.h>
117 #endif
118 #ifdef KERBEROS5
119 #include <com_err.h>
120 #include <krb5/krb5.h>
121 #endif
122
123 #define GLOBAL
124 #include "extern.h"
125 #include "pathnames.h"
126 #include "version.h"
127
128 int     data;
129 jmp_buf urgcatch;
130 int     sflag;
131 int     stru;                   /* avoid C keyword */
132 int     mode;
133 int     dataport;               /* use specific data port */
134 int     dopidfile;              /* maintain pid file */
135 int     doutmp;                 /* update utmp file */
136 int     dowtmp;                 /* update wtmp file */
137 int     doxferlog;              /* syslog wu-ftpd style xferlog entries */
138 int     dropprivs;              /* if privileges should or have been dropped */
139 int     mapped;                 /* IPv4 connection on AF_INET6 socket */
140 off_t   file_size;
141 off_t   byte_count;
142 static char ttyline[20];
143 static struct utmp utmp;        /* for utmp */
144
145 static const char *anondir = NULL;
146 static const char *confdir = _DEFAULT_CONFDIR;
147
148 #if defined(KERBEROS) || defined(KERBEROS5)
149 int     has_ccache = 0;
150 int     notickets = 1;
151 char    *krbtkfile_env = NULL;
152 char    *tty = ttyline;
153 int     login_krb5_forwardable_tgt = 0;
154 #endif
155
156 int epsvall = 0;
157
158 /*
159  * Timeout intervals for retrying connections
160  * to hosts that don't accept PORT cmds.  This
161  * is a kludge, but given the problems with TCP...
162  */
163 #define SWAITMAX        90      /* wait at most 90 seconds */
164 #define SWAITINT        5       /* interval between retries */
165
166 int     swaitmax = SWAITMAX;
167 int     swaitint = SWAITINT;
168
169 static int       bind_pasv_addr(void);
170 static int       checkuser(const char *, const char *, int, int, char **);
171 static int       checkaccess(const char *);
172 static int       checkpassword(const struct passwd *, const char *);
173 static void      end_login(void);
174 static FILE     *getdatasock(const char *);
175 static char     *gunique(const char *);
176 static void      logremotehost(struct sockinet *);
177 static void      lostconn(int);
178 static void      myoob(int);
179 static int       receive_data(FILE *, FILE *);
180 static int       send_data(FILE *, FILE *, off_t, int);
181 static struct passwd *sgetpwnam(const char *);
182
183 int     main(int, char *[]);
184
185 #if defined(KERBEROS)
186 int     klogin(struct passwd *, char *, char *, char *);
187 void    kdestroy(void);
188 #endif
189 #if defined(KERBEROS5)
190 int     k5login(struct passwd *, char *, char *, char *);
191 void    k5destroy(void);
192 #endif
193
194 char * __progname;
195
196 int
197 main(int argc, char *argv[])
198 {
199         int             addrlen, ch, on = 1, tos, keepalive;
200 #ifdef KERBEROS5
201         krb5_error_code kerror;
202 #endif
203         char            *p;
204
205         __progname  = strrchr(argv[0], '/');
206         if (__progname == NULL)
207                 __progname = argv[0];
208         else
209                 __progname++;
210
211         connections = 1;
212         debug = 0;
213         logging = 0;
214         pdata = -1;
215         sflag = 0;
216         dataport = 0;
217         dopidfile = 1;          /* default: DO use a pid file to count users */
218         doutmp = 0;             /* default: Do NOT log to utmp */
219         dowtmp = 1;             /* default: DO log to wtmp */
220         doxferlog = 0;          /* default: Do NOT syslog xferlog */
221         dropprivs = 0;
222         mapped = 0;
223         usedefault = 1;
224         emailaddr = NULL;
225         hostname[0] = '\0';
226         homedir[0] = '\0';
227         gidcount = 0;
228         is_oob = 0;
229         version = FTPD_VERSION;
230
231         /*
232          * LOG_NDELAY sets up the logging connection immediately,
233          * necessary for anonymous ftp's that chroot and can't do it later.
234          */
235         openlog("ftpd", LOG_PID | LOG_NDELAY, FTPD_LOGTYPE);
236
237         while ((ch = getopt(argc, argv, "a:c:C:de:h:HlP:qQrst:T:uUvV:wWX"))
238             != -1) {
239                 switch (ch) {
240                 case 'a':
241                         anondir = optarg;
242                         break;
243
244                 case 'c':
245                         confdir = optarg;
246                         break;
247
248                 case 'C':
249                         pw = sgetpwnam(optarg);
250                         exit(checkaccess(optarg) ? 0 : 1);
251                         /* NOTREACHED */
252
253                 case 'd':
254                 case 'v':               /* deprecated */
255                         debug = 1;
256                         break;
257
258                 case 'e':
259                         emailaddr = optarg;
260                         break;
261
262                 case 'h':
263                         strlcpy(hostname, optarg, sizeof(hostname));
264                         break;
265
266                 case 'H':
267                         if (gethostname(hostname, sizeof(hostname)) == -1)
268                                 hostname[0] = '\0';
269                         hostname[sizeof(hostname) - 1] = '\0';
270                         break;
271
272                 case 'l':
273                         logging++;      /* > 1 == extra logging */
274                         break;
275
276                 case 'P':
277                         dataport = (int)strtol(optarg, &p, 10);
278                         if (*p != '\0' || dataport < IPPORT_RESERVED ||
279                             dataport > IPPORT_ANONMAX) {
280                                 syslog(LOG_WARNING, "Invalid dataport %s",
281                                     optarg);
282                                 dataport = 0;
283                         }
284                         break;
285
286                 case 'q':
287                         dopidfile = 1;
288                         break;
289
290                 case 'Q':
291                         dopidfile = 0;
292                         break;
293
294                 case 'r':
295                         dropprivs = 1;
296                         break;
297
298                 case 's':
299                         sflag = 1;
300                         break;
301
302                 case 't':
303                 case 'T':
304                         syslog(LOG_WARNING,
305                             "-%c has been deprecated in favour of ftpd.conf",
306                             ch);
307                         break;
308
309                 case 'u':
310                         doutmp = 1;
311                         break;
312
313                 case 'U':
314                         doutmp = 0;
315                         break;
316
317                 case 'V':
318                         if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
319                                 version = NULL;
320                         else
321                                 version = xstrdup(optarg);
322                         break;
323
324                 case 'w':
325                         dowtmp = 1;
326                         break;
327
328                 case 'W':
329                         dowtmp = 0;
330                         break;
331
332                 case 'X':
333                         doxferlog = 1;
334                         break;
335
336                 default:
337                         if (optopt == 'a' || optopt == 'C')
338                                 exit(1);
339                         syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
340                         break;
341                 }
342         }
343         if (EMPTYSTR(confdir))
344                 confdir = _DEFAULT_CONFDIR;
345
346         memset((char *)&his_addr, 0, sizeof(his_addr));
347         addrlen = sizeof(his_addr.si_su);
348         if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
349                 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
350                 exit(1);
351         }
352         his_addr.su_len = addrlen;
353         memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr));
354         addrlen = sizeof(ctrl_addr.si_su);
355         if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
356                 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
357                 exit(1);
358         }
359         ctrl_addr.su_len = addrlen;
360 #ifdef INET6
361         if (his_addr.su_family == AF_INET6
362          && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) {
363 #if 1
364                 /*
365                  * IPv4 control connection arrived to AF_INET6 socket.
366                  * I hate to do this, but this is the easiest solution.
367                  *
368                  * The assumption is untrue on SIIT environment.
369                  */
370                 struct sockinet tmp_addr;
371                 const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
372
373                 tmp_addr = his_addr;
374                 memset(&his_addr, 0, sizeof(his_addr));
375                 his_addr.su_family = AF_INET;
376                 his_addr.su_len = sizeof(his_addr.si_su.su_sin);
377                 memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
378                     sizeof(his_addr.su_addr));
379                 his_addr.su_port = tmp_addr.su_port;
380
381                 tmp_addr = ctrl_addr;
382                 memset(&ctrl_addr, 0, sizeof(ctrl_addr));
383                 ctrl_addr.su_family = AF_INET;
384                 ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin);
385                 memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
386                     sizeof(ctrl_addr.su_addr));
387                 ctrl_addr.su_port = tmp_addr.su_port;
388 #else
389                 while (fgets(line, sizeof(line), fd) != NULL) {
390                         if ((cp = strchr(line, '\n')) != NULL)
391                                 *cp = '\0';
392                         reply(-530, "%s", line);
393                 }
394                 (void) fflush(stdout);
395                 (void) fclose(fd);
396                 reply(530,
397                     "Connection from IPv4 mapped address is not supported.");
398                 exit(0);
399 #endif
400
401                 mapped = 1;
402         } else
403 #endif /* INET6 */
404                 mapped = 0;
405 #ifdef IP_TOS
406         if (!mapped && his_addr.su_family == AF_INET) {
407                 tos = IPTOS_LOWDELAY;
408                 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
409                                sizeof(int)) < 0)
410                         syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
411         }
412 #endif
413         /* if the hostname hasn't been given, attempt to determine it */ 
414         if (hostname[0] == '\0') {
415                 if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su,
416                     ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0, 0)
417                     != 0)
418                         (void)gethostname(hostname, sizeof(hostname));
419                 hostname[sizeof(hostname) - 1] = '\0';
420         }
421
422         /* set this here so klogin can use it... */
423         (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
424
425         (void) freopen(_PATH_DEVNULL, "w", stderr);
426         (void) signal(SIGPIPE, lostconn);
427         (void) signal(SIGCHLD, SIG_IGN);
428         if (signal(SIGURG, myoob) == SIG_ERR)
429                 syslog(LOG_WARNING, "signal: %m");
430
431         /* Try to handle urgent data inline */
432 #ifdef SO_OOBINLINE
433         if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
434                 syslog(LOG_WARNING, "setsockopt: %m");
435 #endif
436         /* Set keepalives on the socket to detect dropped connections.  */
437 #ifdef SO_KEEPALIVE
438         keepalive = 1;
439         if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive,
440             sizeof(int)) < 0)
441                 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
442 #endif
443
444 #ifdef  F_SETOWN
445         if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
446                 syslog(LOG_WARNING, "fcntl F_SETOWN: %m");
447 #endif
448         logremotehost(&his_addr);
449         /*
450          * Set up default state
451          */
452         data = -1;
453         type = TYPE_A;
454         form = FORM_N;
455         stru = STRU_F;
456         mode = MODE_S;
457         tmpline[0] = '\0';
458         hasyyerrored = 0;
459
460 #ifdef KERBEROS5
461         kerror = krb5_init_context(&kcontext);
462         if (kerror) {
463                 syslog(LOG_ERR, "%s when initializing Kerberos context",
464                     error_message(kerror));
465                 exit(0);
466         }
467 #endif /* KERBEROS5 */
468
469         init_curclass();
470         curclass.timeout = 300;         /* 5 minutes, as per login(1) */
471         curclass.type = CLASS_REAL;
472
473         /* If logins are disabled, print out the message. */
474         if (display_file(_PATH_NOLOGIN, 530)) {
475                 reply(530, "System not available.");
476                 exit(0);
477         }
478         (void)display_file(conffilename(_PATH_FTPWELCOME), 220);
479                 /* reply(220,) must follow */
480         if (EMPTYSTR(version))
481                 reply(220, "%s FTP server ready.", hostname);
482         else
483                 reply(220, "%s FTP server (%s) ready.", hostname, version);
484
485         (void) setjmp(errcatch);
486         ftp_loop();
487         /* NOTREACHED */
488 }
489
490 static void
491 lostconn(int signo)
492 {
493
494         if (debug)
495                 syslog(LOG_DEBUG, "lost connection");
496         dologout(1);
497 }
498
499 /*
500  * Save the result of a getpwnam.  Used for USER command, since
501  * the data returned must not be clobbered by any other command
502  * (e.g., globbing).
503  */
504 static struct passwd *
505 sgetpwnam(const char *name)
506 {
507         static struct passwd save;
508         struct passwd *p;
509
510         if ((p = getpwnam(name)) == NULL)
511                 return (p);
512         if (save.pw_name) {
513                 free((char *)save.pw_name);
514                 memset(save.pw_passwd, 0, strlen(save.pw_passwd));
515                 free((char *)save.pw_passwd);
516                 free((char *)save.pw_gecos);
517                 free((char *)save.pw_dir);
518                 free((char *)save.pw_shell);
519         }
520         save = *p;
521         save.pw_name = xstrdup(p->pw_name);
522         save.pw_passwd = xstrdup(p->pw_passwd);
523         save.pw_gecos = xstrdup(p->pw_gecos);
524         save.pw_dir = xstrdup(p->pw_dir);
525         save.pw_shell = xstrdup(p->pw_shell);
526         return (&save);
527 }
528
529 static int      login_attempts; /* number of failed login attempts */
530 static int      askpasswd;      /* had USER command, ask for PASSwd */
531 static int      permitted;      /* USER permitted */
532 static char     curname[10];    /* current USER name */
533
534 /*
535  * USER command.
536  * Sets global passwd pointer pw if named account exists and is acceptable;
537  * sets askpasswd if a PASS command is expected.  If logged in previously,
538  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
539  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
540  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
541  * requesting login privileges.  Disallow anyone who does not have a standard
542  * shell as returned by getusershell().  Disallow anyone mentioned in the file
543  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
544  */
545 void
546 user(const char *name)
547 {
548         char    *class;
549
550         class = NULL;
551         if (logged_in) {
552                 switch (curclass.type) {
553                 case CLASS_GUEST:
554                         reply(530, "Can't change user from guest login.");
555                         return;
556                 case CLASS_CHROOT:
557                         reply(530, "Can't change user from chroot user.");
558                         return;
559                 case CLASS_REAL:
560                         if (dropprivs) {
561                                 reply(530, "Can't change user.");
562                                 return;
563                         }
564                         end_login();
565                         break;
566                 default:
567                         abort();
568                 }
569         }
570
571 #if defined(KERBEROS)
572         kdestroy();
573 #endif
574 #if defined(KERBEROS5)
575         k5destroy();
576 #endif
577
578         curclass.type = CLASS_REAL;
579         askpasswd = 0;
580         permitted = 0;
581
582         if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
583                         /* need `pw' setup for checkaccess() and checkuser () */
584                 if ((pw = sgetpwnam("ftp")) == NULL)
585                         reply(530, "User %s unknown.", name);
586                 else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
587                         reply(530, "User %s access denied.", name);
588                 else {
589                         curclass.type = CLASS_GUEST;
590                         askpasswd = 1;
591                         reply(331,
592                             "Guest login ok, type your name as password.");
593                 }
594                 if (!askpasswd) {
595                         if (logging)
596                                 syslog(LOG_NOTICE,
597                                     "ANONYMOUS FTP LOGIN REFUSED FROM %s",
598                                     remotehost);
599                         end_login();
600                         goto cleanup_user;
601                 }
602                 name = "ftp";
603         } else
604                 pw = sgetpwnam(name);
605
606         if (logging)
607                 strlcpy(curname, name, sizeof(curname));
608
609                         /* check user in /etc/ftpusers, and setup class */
610         permitted = checkuser(_PATH_FTPUSERS, curname, 1, 0, &class);
611
612                         /* check user in /etc/ftpchroot */
613         if (checkuser(_PATH_FTPCHROOT, curname, 0, 0, NULL)) {
614                 if (curclass.type == CLASS_GUEST) {
615                         syslog(LOG_NOTICE,
616             "Can't change guest user to chroot class; remove entry in %s",
617                             _PATH_FTPCHROOT);
618                         exit(1);
619                 }
620                 curclass.type = CLASS_CHROOT;
621         }
622                         /* determine default class */
623         if (class == NULL) {
624                 switch (curclass.type) {
625                 case CLASS_GUEST:
626                         class = xstrdup("guest");
627                         break;
628                 case CLASS_CHROOT:
629                         class = xstrdup("chroot");
630                         break;
631                 case CLASS_REAL:
632                         class = xstrdup("real");
633                         break;
634                 default:
635                         syslog(LOG_ERR, "unknown curclass.type %d; aborting",
636                             curclass.type);
637                         abort();
638                 }
639         }
640                         /* parse ftpd.conf, setting up various parameters */
641         parse_conf(class);
642                         /* if not guest user, check for valid shell */
643         if (pw == NULL)
644                 permitted = 0;
645         else {
646                 const char      *cp, *shell;
647
648                 if ((shell = pw->pw_shell) == NULL || *shell == 0)
649                         shell = _PATH_BSHELL;
650                 while ((cp = getusershell()) != NULL)
651                         if (strcmp(cp, shell) == 0)
652                                 break;
653                 endusershell();
654                 if (cp == NULL && curclass.type != CLASS_GUEST)
655                         permitted = 0;
656         }
657
658                         /* deny quickly (after USER not PASS) if requested */
659         if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) {
660                 reply(530, "User %s may not use FTP.", curname);
661                 if (logging)
662                         syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
663                             remotehost, curname);
664                 end_login();
665                 goto cleanup_user;
666         }
667
668                         /* if haven't asked yet (i.e, not anon), ask now */
669         if (!askpasswd) {
670                 askpasswd = 1;
671 #ifdef SKEY
672                 if (skey_haskey(curname) == 0) {
673                         const char *myskey;
674
675                         myskey = skey_keyinfo(curname);
676                         reply(331, "Password [ %s ] required for %s.",
677                             myskey ? myskey : "error getting challenge",
678                             curname);
679                 } else
680 #endif
681                         reply(331, "Password required for %s.", curname);
682         }
683
684  cleanup_user:
685         /*
686          * Delay before reading passwd after first failed
687          * attempt to slow down passwd-guessing programs.
688          */
689         if (login_attempts)
690                 sleep((unsigned) login_attempts);
691
692         if (class)
693                 free(class);
694 }
695
696 /*
697  * Determine whether something is to happen (allow access, chroot)
698  * for a user. Each line is a shell-style glob followed by
699  * `yes' or `no'.
700  *
701  * For backward compatibility, `allow' and `deny' are synonymns
702  * for `yes' and `no', respectively.
703  *
704  * Each glob is matched against the username in turn, and the first
705  * match found is used. If no match is found, the result is the
706  * argument `def'. If a match is found but without and explicit
707  * `yes'/`no', the result is the opposite of def.
708  *
709  * If the file doesn't exist at all, the result is the argument
710  * `nofile'
711  *
712  * Any line starting with `#' is considered a comment and ignored.
713  *
714  * Returns 0 if the user is denied, or 1 if they are allowed.
715  *
716  * NOTE: needs struct passwd *pw setup before use.
717  */
718 static int
719 checkuser(const char *fname, const char *name, int def, int nofile,
720             char **retclass)
721 {
722         FILE    *fd;
723         int      retval;
724         char    *word, *perm, *class, *buf, *p;
725         size_t   len, line;
726
727         retval = def;
728         if (retclass != NULL)
729                 *retclass = NULL;
730         if ((fd = fopen(conffilename(fname), "r")) == NULL)
731                 return nofile;
732
733         line = 0;
734         for (;
735             (buf = fparseln(fd, &len, &line, NULL, FPARSELN_UNESCCOMM |
736                         FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
737             free(buf), buf = NULL) {
738                 word = perm = class = NULL;
739                 p = buf;
740                 if (len < 1)
741                         continue;
742                 if (p[len - 1] == '\n')
743                         p[--len] = '\0';
744                 if (EMPTYSTR(p))
745                         continue;
746
747                 NEXTWORD(p, word);
748                 NEXTWORD(p, perm);
749                 NEXTWORD(p, class);
750                 if (EMPTYSTR(word))
751                         continue;
752                 if (!EMPTYSTR(class)) {
753                         if (strcasecmp(class, "all") == 0 ||
754                             strcasecmp(class, "none") == 0) {
755                                 syslog(LOG_WARNING,
756                 "%s line %d: illegal user-defined class `%s' - skipping entry",
757                                             fname, (int)line, class);
758                                 continue;
759                         }
760                 }
761
762                                         /* have a host specifier */
763                 if ((p = strchr(word, '@')) != NULL) {
764                         unsigned long   net, mask, addr;
765                         int             bits;
766
767                         *p++ = '\0';
768                                         /* check against network or CIDR */
769                         if (isdigit(*p) &&
770                             (bits = inet_net_pton(AF_INET, p,
771                             &net, sizeof(net))) != -1) {
772                                 net = ntohl(net);
773                                 mask = 0xffffffffU << (32 - bits);
774                                 addr = ntohl(his_addr.su_addr.s_addr);
775                                 if ((addr & mask) != net)
776                                         continue;
777
778                                         /* check against hostname glob */
779                         } else if (fnmatch(p, remotehost, FNM_CASEFOLD) != 0)
780                                 continue;
781                 }
782
783                                         /* have a group specifier */
784                 if ((p = strchr(word, ':')) != NULL) {
785                         gid_t   *groups, *ng;
786                         int      gsize, i, found;
787
788                         if (pw == NULL)
789                                 continue;       /* no match for unknown user */
790                         *p++ = '\0';
791                         groups = NULL;
792                         gsize = 16;
793                         do {
794                                 ng = realloc(groups, gsize * sizeof(gid_t));
795                                 if (ng == NULL)
796                                         fatal(
797                                             "Local resource failure: realloc");
798                                 groups = ng;
799                         } while (getgrouplist(pw->pw_name, pw->pw_gid,
800                                                 groups, &gsize) == -1);
801                         found = 0;
802                         for (i = 0; i < gsize; i++) {
803                                 struct group *g;
804
805                                 if ((g = getgrgid(groups[i])) == NULL)
806                                         continue;
807                                 if (fnmatch(p, g->gr_name, 0) == 0) {
808                                         found = 1;
809                                         break;
810                                 }
811                         }
812                         free(groups);
813                         if (!found)
814                                 continue;
815                 }
816
817                                         /* check against username glob */
818                 if (fnmatch(word, name, 0) != 0)
819                         continue;
820
821                 if (perm != NULL &&
822                     ((strcasecmp(perm, "allow") == 0) ||
823                      (strcasecmp(perm, "yes") == 0)))
824                         retval = 1;
825                 else if (perm != NULL &&
826                     ((strcasecmp(perm, "deny") == 0) ||
827                      (strcasecmp(perm, "no") == 0)))
828                         retval = 0;
829                 else
830                         retval = !def;
831                 if (!EMPTYSTR(class) && retclass != NULL)
832                         *retclass = xstrdup(class);
833                 free(buf);
834                 break;
835         }
836         (void) fclose(fd);
837         return (retval);
838 }
839
840 /*
841  * Check if user is allowed by /etc/ftpusers
842  * returns 1 for yes, 0 for no
843  *
844  * NOTE: needs struct passwd *pw setup (for checkuser())
845  */
846 static int
847 checkaccess(const char *name)
848 {
849
850         return (checkuser(_PATH_FTPUSERS, name, 1, 0, NULL));
851 }
852
853 /*
854  * Terminate login as previous user (if any), resetting state;
855  * used when USER command is given or login fails.
856  */
857 static void
858 end_login(void)
859 {
860
861         if (logged_in) {
862 #ifdef NO_UTMP
863                 if (dowtmp)
864                         logwtmp(ttyline, "", "");
865                 if (doutmp)
866                         logout(utmp.ut_line);
867 #endif /* NO_UTMP */
868         }
869                         /* reset login state */
870         show_chdir_messages(-1);                /* flush chdir cache */
871         if (pw != NULL && pw->pw_passwd != NULL)
872                 memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
873         pw = NULL;
874         logged_in = 0;
875         askpasswd = 0;
876         permitted = 0;
877         quietmessages = 0;
878         gidcount = 0;
879         curclass.type = CLASS_REAL;
880         (void) seteuid((uid_t)0);
881 }
882
883 void
884 pass(const char *passwd)
885 {
886         int              rval;
887         char             root[MAXPATHLEN];
888         char            *p;
889         int              len;
890
891         if (logged_in || askpasswd == 0) {
892                 reply(503, "Login with USER first.");
893                 return;
894         }
895         askpasswd = 0;
896         if (curclass.type != CLASS_GUEST) {
897                         /* "ftp" is the only account allowed with no password */
898                 if (pw == NULL) {
899                         rval = 1;       /* failure below */
900                         goto skip;
901                 }
902 #if defined(KERBEROS)
903                 if (klogin(pw, "", hostname, (char *)passwd) == 0) {
904                         rval = 0;
905                         goto skip;
906                 }
907 #endif
908 #if defined(KERBEROS5)
909                 if (k5login(pw, "", hostname, (char *)passwd) == 0) {
910                         rval = 0;
911                         goto skip;
912                 }
913 #endif
914 #ifdef SKEY
915                 if (skey_haskey(pw->pw_name) == 0) {
916                         char *p;
917                         int r;
918
919                         p = xstrdup(passwd);
920                         r = skey_passcheck(pw->pw_name, p);
921                         free(p);
922                         if (r != -1) {
923                                 rval = 0;
924                                 goto skip;
925                         }
926                 }
927 #endif
928                 if (!sflag)
929                         rval = checkpassword(pw, passwd);
930                 else
931                         rval = 1;
932
933  skip:
934
935                         /*
936                          * If rval > 0, the user failed the authentication check
937                          * above.  If rval == 0, either Kerberos or local
938                          * authentication succeeded.
939                          */
940                 if (rval) {
941                         reply(530, "%s", rval == 2 ? "Password expired." :
942                             "Login incorrect.");
943                         if (logging) {
944                                 syslog(LOG_NOTICE,
945                                     "FTP LOGIN FAILED FROM %s", remotehost);
946                                 syslog(LOG_AUTHPRIV | LOG_NOTICE,
947                                     "FTP LOGIN FAILED FROM %s, %s",
948                                     remotehost, curname);
949                         }
950                         pw = NULL;
951                         if (login_attempts++ >= 5) {
952                                 syslog(LOG_NOTICE,
953                                     "repeated login failures from %s",
954                                     remotehost);
955                                 exit(0);
956                         }
957                         return;
958                 }
959         }
960
961                         /* password ok; check if anything else prevents login */
962         if (! permitted) {
963                 reply(530, "User %s may not use FTP.", pw->pw_name);
964                 if (logging)
965                         syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
966                             remotehost, pw->pw_name);
967                 goto bad;
968         }
969
970         login_attempts = 0;             /* this time successful */
971         if (setegid((gid_t)pw->pw_gid) < 0) {
972                 reply(550, "Can't set gid.");
973                 goto bad;
974         }
975         (void) initgroups(pw->pw_name, pw->pw_gid);
976                         /* cache groups for cmds.c::matchgroup() */
977         gidcount = getgroups(sizeof(gidlist), gidlist);
978
979                         /* open wtmp before chroot */
980 #ifdef NO_UTMP
981         if (dowtmp)
982                 logwtmp(ttyline, pw->pw_name, remotehost);
983
984                         /* open utmp before chroot */
985         if (doutmp) {
986                 memset((void *)&utmp, 0, sizeof(utmp));
987                 (void)time(&utmp.ut_time);
988                 (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name));
989                 (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host));
990                 (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line));
991                 login(&utmp);
992         }
993 #endif /* NO_UTMP */
994
995         logged_in = 1;
996
997         connections = 1;
998         if (dopidfile)
999                 count_users();
1000         if (curclass.limit != -1 && connections > curclass.limit) {
1001                 if (! EMPTYSTR(curclass.limitfile))
1002                         (void)display_file(conffilename(curclass.limitfile),
1003                             530);
1004                 reply(530,
1005                     "User %s access denied, connection limit of %d reached.",
1006                     pw->pw_name, curclass.limit);
1007                 syslog(LOG_NOTICE,
1008     "Maximum connection limit of %d for class %s reached, login refused for %s",
1009                     curclass.limit, curclass.classname, pw->pw_name);
1010                 goto bad;
1011         }
1012
1013         homedir[0] = '/';
1014         switch (curclass.type) {
1015         case CLASS_GUEST:
1016                         /*
1017                          * We MUST do a chdir() after the chroot. Otherwise
1018                          * the old current directory will be accessible as "."
1019                          * outside the new root!
1020                          */
1021                 format_path(root,
1022                     curclass.chroot ? curclass.chroot :
1023                     anondir ? anondir :
1024                     pw->pw_dir);
1025                 format_path(homedir,
1026                     curclass.homedir ? curclass.homedir :
1027                     "/");
1028                 if (EMPTYSTR(homedir))
1029                         homedir[0] = '/';
1030                 if (EMPTYSTR(root) || chroot(root) < 0) {
1031                         syslog(LOG_NOTICE,
1032                             "GUEST user %s: can't chroot to %s: %m",
1033                             pw->pw_name, root);
1034                         goto bad_guest;
1035                 }
1036                 if (chdir(homedir) < 0) {
1037                         syslog(LOG_NOTICE,
1038                             "GUEST user %s: can't chdir to %s: %m",
1039                             pw->pw_name, homedir);
1040  bad_guest:
1041                         reply(550, "Can't set guest privileges.");
1042                         goto bad;
1043                 }
1044                 break;
1045         case CLASS_CHROOT:
1046                 format_path(root,
1047                     curclass.chroot ? curclass.chroot :
1048                     pw->pw_dir);
1049                 format_path(homedir,
1050                     curclass.homedir ? curclass.homedir :
1051                     "/");
1052                 if (EMPTYSTR(homedir))
1053                         homedir[0] = '/';
1054                 if (EMPTYSTR(root) || chroot(root) < 0) {
1055                         syslog(LOG_NOTICE,
1056                             "CHROOT user %s: can't chroot to %s: %m",
1057                             pw->pw_name, root);
1058                         goto bad_chroot;
1059                 }
1060                 if (chdir(homedir) < 0) {
1061                         syslog(LOG_NOTICE,
1062                             "CHROOT user %s: can't chdir to %s: %m",
1063                             pw->pw_name, homedir);
1064  bad_chroot:
1065                         reply(550, "Can't change root.");
1066                         goto bad;
1067                 }
1068                 break;
1069         case CLASS_REAL:
1070                         /* only chroot REAL if explictly requested */
1071                 if (! EMPTYSTR(curclass.chroot)) {
1072                         format_path(root, curclass.chroot);
1073                         if (EMPTYSTR(root) || chroot(root) < 0) {
1074                                 syslog(LOG_NOTICE,
1075                                     "REAL user %s: can't chroot to %s: %m",
1076                                     pw->pw_name, root);
1077                                 goto bad_chroot;
1078                         }
1079                 }
1080                 format_path(homedir,
1081                     curclass.homedir ? curclass.homedir :
1082                     pw->pw_dir);
1083                 if (EMPTYSTR(homedir) || chdir(homedir) < 0) {
1084                         if (chdir("/") < 0) {
1085                                 syslog(LOG_NOTICE,
1086                                     "REAL user %s: can't chdir to %s: %m",
1087                                     pw->pw_name,
1088                                     !EMPTYSTR(homedir) ?  homedir : "/");
1089                                 reply(530,
1090                                     "User %s: can't change directory to %s.",
1091                                     pw->pw_name,
1092                                     !EMPTYSTR(homedir) ? homedir : "/");
1093                                 goto bad;
1094                         } else {
1095                                 reply(-230,
1096                                     "No directory! Logging in with home=/");
1097                                 homedir[0] = '/';
1098                         }
1099                 }
1100                 break;
1101         }
1102 #if HAVE_SETLOGIN
1103         setlogin(pw->pw_name);
1104 #endif
1105         if (dropprivs ||
1106             (curclass.type != CLASS_REAL && 
1107             ntohs(ctrl_addr.su_port) > IPPORT_RESERVED + 1)) {
1108                 dropprivs++;
1109                 if (setgid((gid_t)pw->pw_gid) < 0) {
1110                         reply(550, "Can't set gid.");
1111                         goto bad;
1112                 }
1113                 if (setuid((uid_t)pw->pw_uid) < 0) {
1114                         reply(550, "Can't set uid.");
1115                         goto bad;
1116                 }
1117         } else {
1118                 if (seteuid((uid_t)pw->pw_uid) < 0) {
1119                         reply(550, "Can't set uid.");
1120                         goto bad;
1121                 }
1122         }
1123         len = sizeof("HOME=") + strlen(homedir) + 1;;
1124         p = malloc(len);
1125         if (p == NULL) {
1126                 reply(550, "Local resource failure: malloc");
1127                 goto bad;
1128         }
1129         snprintf(p, len, "HOME=%s", homedir);
1130         putenv(p);
1131         free(p);
1132
1133         if (curclass.type == CLASS_GUEST && passwd[0] == '-')
1134                 quietmessages = 1;
1135
1136                         /*
1137                          * Display a login message, if it exists.
1138                          * N.B. reply(230,) must follow the message.
1139                          */
1140         if (! EMPTYSTR(curclass.motd))
1141                 (void)display_file(conffilename(curclass.motd), 230);
1142         show_chdir_messages(230);
1143         if (curclass.type == CLASS_GUEST) {
1144                 char *p;
1145
1146                 reply(230, "Guest login ok, access restrictions apply.");
1147 #if HAVE_SETPROCTITLE
1148                 snprintf(proctitle, sizeof(proctitle),
1149                     "%s: anonymous/%s", remotehost, passwd);
1150                 setproctitle("%s", proctitle);
1151 #endif /* HAVE_SETPROCTITLE */
1152                 if (logging)
1153                         syslog(LOG_INFO,
1154                         "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1155                             remotehost, passwd,
1156                             curclass.classname, CURCLASSTYPE);
1157                         /* store guest password reply into pw_passwd */
1158                 REASSIGN(pw->pw_passwd, xstrdup(passwd));
1159                 for (p = pw->pw_passwd; *p; p++)
1160                         if (!isgraph(*p))
1161                                 *p = '_';
1162         } else {
1163                 reply(230, "User %s logged in.", pw->pw_name);
1164 #if HAVE_SETPROCTITLE
1165                 snprintf(proctitle, sizeof(proctitle),
1166                     "%s: %s", remotehost, pw->pw_name);
1167                 setproctitle("%s", proctitle);
1168 #endif /* HAVE_SETPROCTITLE */
1169                 if (logging)
1170                         syslog(LOG_INFO,
1171                             "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1172                             remotehost, pw->pw_name,
1173                             curclass.classname, CURCLASSTYPE);
1174         }
1175         (void) umask(curclass.umask);
1176         return;
1177
1178  bad:
1179                         /* Forget all about it... */
1180         end_login();
1181 }
1182
1183 void
1184 retrieve(char *argv[], const char *name)
1185 {
1186         FILE *fin, *dout;
1187         struct stat st;
1188         int (*closefunc)(FILE *) = NULL;
1189         int log, sendrv, closerv, stderrfd, isconversion, isdata, isls;
1190         struct timeval start, finish, td, *tdp;
1191         const char *dispname;
1192         char *error;
1193
1194         sendrv = closerv = stderrfd = -1;
1195         isconversion = isdata = isls = log = 0;
1196         tdp = NULL;
1197         dispname = name;
1198         fin = dout = NULL;
1199         error = NULL;
1200         if (argv == NULL) {             /* if not running a command ... */
1201                 log = 1;
1202                 isdata = 1;
1203                 fin = fopen(name, "r");
1204                 closefunc = fclose;
1205                 if (fin == NULL)        /* doesn't exist?; try a conversion */
1206                         argv = do_conversion(name);
1207                 if (argv != NULL) {
1208                         isconversion++;
1209                         syslog(LOG_DEBUG, "get command: '%s' on '%s'",
1210                             argv[0], name);
1211                 }
1212         }
1213         if (argv != NULL) {
1214                 char temp[MAXPATHLEN];
1215
1216                 if (strcmp(argv[0], INTERNAL_LS) == 0) {
1217                         isls = 1;
1218                         stderrfd = -1;
1219                 } else {
1220                         (void)snprintf(temp, sizeof(temp), "%s", TMPFILE);
1221                         stderrfd = mkstemp(temp);
1222                         if (stderrfd != -1)
1223                                 (void)unlink(temp);
1224                 }
1225                 dispname = argv[0];
1226                 fin = ftpd_popen(argv, "r", stderrfd);
1227                 closefunc = ftpd_pclose;
1228                 st.st_size = -1;
1229                 st.st_blksize = BUFSIZ;
1230         }
1231         if (fin == NULL) {
1232                 if (errno != 0) {
1233                         perror_reply(550, dispname);
1234                         if (log)
1235                                 logxfer("get", -1, name, NULL, NULL,
1236                                     strerror(errno));
1237                 }
1238                 goto cleanupretrieve;
1239         }
1240         byte_count = -1;
1241         if (argv == NULL
1242             && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1243                 error = "Not a plain file";
1244                 reply(550, "%s: %s.", dispname, error);
1245                 goto done;
1246         }
1247         if (restart_point) {
1248                 if (type == TYPE_A) {
1249                         off_t i;
1250                         int c;
1251
1252                         for (i = 0; i < restart_point; i++) {
1253                                 if ((c=getc(fin)) == EOF) {
1254                                         error = strerror(errno);
1255                                         perror_reply(550, dispname);
1256                                         goto done;
1257                                 }
1258                                 if (c == '\n')
1259                                         i++;
1260                         }
1261                 } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1262                         error = strerror(errno);
1263                         perror_reply(550, dispname);
1264                         goto done;
1265                 }
1266         }
1267         dout = dataconn(dispname, st.st_size, "w");
1268         if (dout == NULL)
1269                 goto done;
1270
1271         (void)gettimeofday(&start, NULL);
1272         sendrv = send_data(fin, dout, st.st_blksize, isdata);
1273         (void)gettimeofday(&finish, NULL);
1274         closedataconn(dout);            /* close now to affect timing stats */
1275         timersub(&finish, &start, &td);
1276         tdp = &td;
1277  done:
1278         if (log)
1279                 logxfer("get", byte_count, name, NULL, tdp, error);
1280         closerv = (*closefunc)(fin);
1281         if (sendrv == 0) {
1282                 FILE *errf;
1283                 struct stat sb;
1284
1285                 if (!isls && argv != NULL && closerv != 0) {
1286                         reply(-226,
1287                             "Command returned an exit status of %d",
1288                             closerv);
1289                         if (isconversion)
1290                                 syslog(LOG_WARNING,
1291                                     "retrieve command: '%s' returned %d",
1292                                     argv[0], closerv);
1293                 }
1294                 if (!isls && argv != NULL && stderrfd != -1 &&
1295                     (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 &&
1296                     ((errf = fdopen(stderrfd, "r")) != NULL)) {
1297                         char *cp, line[LINE_MAX];
1298
1299                         reply(-226, "Command error messages:");
1300                         rewind(errf);
1301                         while (fgets(line, sizeof(line), errf) != NULL) {
1302                                 if ((cp = strchr(line, '\n')) != NULL)
1303                                         *cp = '\0';
1304                                 reply(0, "  %s", line);
1305                         }
1306                         (void) fflush(stdout);
1307                         (void) fclose(errf);
1308                                 /* a reply(226,) must follow */
1309                 }
1310                 reply(226, "Transfer complete.");
1311         }
1312  cleanupretrieve:
1313         if (stderrfd != -1)
1314                 (void)close(stderrfd);
1315         if (isconversion)
1316                 free(argv);
1317 }
1318
1319 void
1320 store(const char *name, const char *fmode, int unique)
1321 {
1322         FILE *fout, *din;
1323         struct stat st;
1324         int (*closefunc)(FILE *);
1325         struct timeval start, finish, td, *tdp;
1326         char *desc, *error;
1327
1328         din = NULL;
1329         desc = (*fmode == 'w') ? "put" : "append";
1330         error = NULL;
1331         if (unique && stat(name, &st) == 0 &&
1332             (name = gunique(name)) == NULL) {
1333                 logxfer(desc, -1, name, NULL, NULL,
1334                     "cannot create unique file");
1335                 goto cleanupstore;
1336         }
1337
1338         if (restart_point)
1339                 fmode = "r+";
1340         fout = fopen(name, fmode);
1341         closefunc = fclose;
1342         tdp = NULL;
1343         if (fout == NULL) {
1344                 perror_reply(553, name);
1345                 logxfer(desc, -1, name, NULL, NULL, strerror(errno));
1346                 goto cleanupstore;
1347         }
1348         byte_count = -1;
1349         if (restart_point) {
1350                 if (type == TYPE_A) {
1351                         off_t i;
1352                         int c;
1353
1354                         for (i = 0; i < restart_point; i++) {
1355                                 if ((c=getc(fout)) == EOF) {
1356                                         error = strerror(errno);
1357                                         perror_reply(550, name);
1358                                         goto done;
1359                                 }
1360                                 if (c == '\n')
1361                                         i++;
1362                         }
1363                         /*
1364                          * We must do this seek to "current" position
1365                          * because we are changing from reading to
1366                          * writing.
1367                          */
1368                         if (fseek(fout, 0L, SEEK_CUR) < 0) {
1369                                 error = strerror(errno);
1370                                 perror_reply(550, name);
1371                                 goto done;
1372                         }
1373                 } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1374                         error = strerror(errno);
1375                         perror_reply(550, name);
1376                         goto done;
1377                 }
1378         }
1379         din = dataconn(name, (off_t)-1, "r");
1380         if (din == NULL)
1381                 goto done;
1382         (void)gettimeofday(&start, NULL);
1383         if (receive_data(din, fout) == 0) {
1384                 if (unique)
1385                         reply(226, "Transfer complete (unique file name:%s).",
1386                             name);
1387                 else
1388                         reply(226, "Transfer complete.");
1389         }
1390         (void)gettimeofday(&finish, NULL);
1391         closedataconn(din);             /* close now to affect timing stats */
1392         timersub(&finish, &start, &td);
1393         tdp = &td;
1394  done:
1395         logxfer(desc, byte_count, name, NULL, tdp, error);
1396         (*closefunc)(fout);
1397  cleanupstore:
1398         ;
1399 }
1400
1401 static FILE *
1402 getdatasock(const char *fmode)
1403 {
1404         int             on, s, t, tries;
1405         in_port_t       port;
1406
1407         on = 1;
1408         if (data >= 0)
1409                 return (fdopen(data, fmode));
1410         if (! dropprivs)
1411                 (void) seteuid((uid_t)0);
1412         s = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
1413         if (s < 0)
1414                 goto bad;
1415         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1416             (char *) &on, sizeof(on)) < 0)
1417                 goto bad;
1418         if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1419             (char *) &on, sizeof(on)) < 0)
1420                 goto bad;
1421                         /* anchor socket to avoid multi-homing problems */
1422         data_source = ctrl_addr;
1423                         /*
1424                          * By default source port for PORT connctions is
1425                          * ctrlport-1 (see RFC959 section 5.2).
1426                          * However, if privs have been dropped and that
1427                          * would be < IPPORT_RESERVED, use a random port
1428                          * instead.
1429                          */
1430         if (dataport)
1431                 port = dataport;
1432         else
1433                 port = ntohs(ctrl_addr.su_port) - 1;
1434         if (dropprivs && port < IPPORT_RESERVED)
1435                 port = 0;               /* use random port */
1436         data_source.su_port = htons(port);
1437
1438         for (tries = 1; ; tries++) {
1439                 if (bind(s, (struct sockaddr *)&data_source.si_su,
1440                     data_source.su_len) >= 0)
1441                         break;
1442                 if (errno != EADDRINUSE || tries > 10)
1443                         goto bad;
1444                 sleep(tries);
1445         }
1446         if (! dropprivs)
1447                 (void) seteuid((uid_t)pw->pw_uid);
1448 #ifdef IP_TOS
1449         if (!mapped && ctrl_addr.su_family == AF_INET) {
1450                 on = IPTOS_THROUGHPUT;
1451                 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on,
1452                                sizeof(int)) < 0)
1453                         syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1454         }
1455 #endif
1456         return (fdopen(s, fmode));
1457  bad:
1458                 /* Return the real value of errno (close may change it) */
1459         t = errno;
1460         if (! dropprivs)
1461                 (void) seteuid((uid_t)pw->pw_uid);
1462         (void) close(s);
1463         errno = t;
1464         return (NULL);
1465 }
1466
1467 FILE *
1468 dataconn(const char *name, off_t size, const char *fmode)
1469 {
1470         char sizebuf[32];
1471         FILE *file;
1472         int retry = 0, tos, keepalive;
1473
1474         file_size = size;
1475         byte_count = 0;
1476         if (size != (off_t) -1)
1477                 (void)snprintf(sizebuf, sizeof(sizebuf), " (" LLF " byte%s)",
1478                     (LLT)size, PLURAL(size));
1479         else
1480                 sizebuf[0] = '\0';
1481         if (pdata >= 0) {
1482                 struct sockinet from;
1483                 int s, fromlen = sizeof(from.su_len);
1484
1485                 (void) alarm(curclass.timeout);
1486                 s = accept(pdata, (struct sockaddr *)&from.si_su, &fromlen);
1487                 (void) alarm(0);
1488                 if (s < 0) {
1489                         reply(425, "Can't open data connection.");
1490                         (void) close(pdata);
1491                         pdata = -1;
1492                         return (NULL);
1493                 }
1494                 (void) close(pdata);
1495                 pdata = s;
1496                 switch (from.su_family) {
1497                 case AF_INET:
1498 #ifdef IP_TOS
1499                         if (!mapped) {
1500                                 tos = IPTOS_THROUGHPUT;
1501                                 (void) setsockopt(s, IPPROTO_IP, IP_TOS,
1502                                     (char *)&tos, sizeof(int));
1503                         }
1504                         break;
1505 #endif
1506                 }
1507                 /* Set keepalives on the socket to detect dropped conns. */
1508 #ifdef SO_KEEPALIVE
1509                 keepalive = 1;
1510                 (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1511                     (char *)&keepalive, sizeof(int));
1512 #endif
1513                 reply(150, "Opening %s mode data connection for '%s'%s.",
1514                      type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1515                 return (fdopen(pdata, fmode));
1516         }
1517         if (data >= 0) {
1518                 reply(125, "Using existing data connection for '%s'%s.",
1519                     name, sizebuf);
1520                 usedefault = 1;
1521                 return (fdopen(data, fmode));
1522         }
1523         if (usedefault)
1524                 data_dest = his_addr;
1525         usedefault = 1;
1526         file = getdatasock(fmode);
1527         if (file == NULL) {
1528                 char hbuf[NI_MAXHOST];
1529                 char pbuf[NI_MAXSERV];
1530
1531                 if (getnameinfo((struct sockaddr *)&data_source.si_su,
1532                     data_source.su_len, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
1533                     NI_NUMERICHOST | NI_NUMERICSERV))
1534                         strlcpy(hbuf, "?", sizeof(hbuf));
1535                 reply(425, "Can't create data socket (%s,%s): %s.",
1536                       hbuf, pbuf, strerror(errno));
1537                 return (NULL);
1538         }
1539         data = fileno(file);
1540         while (connect(data, (struct sockaddr *)&data_dest.si_su,
1541             data_dest.su_len) < 0) {
1542                 if (errno == EADDRINUSE && retry < swaitmax) {
1543                         sleep((unsigned) swaitint);
1544                         retry += swaitint;
1545                         continue;
1546                 }
1547                 perror_reply(425, "Can't build data connection");
1548                 (void) fclose(file);
1549                 data = -1;
1550                 return (NULL);
1551         }
1552         reply(150, "Opening %s mode data connection for '%s'%s.",
1553              type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1554         return (file);
1555 }
1556
1557 void
1558 closedataconn(FILE *fd)
1559 {
1560
1561         if (fd == NULL)
1562                 return;
1563         (void)fclose(fd);
1564         data = -1;
1565         if (pdata >= 0)
1566                 (void)close(pdata);
1567         pdata = -1;
1568 }
1569
1570 /*
1571  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1572  * encapsulation of the data subject * to Mode, Structure, and Type.
1573  *
1574  * NB: Form isn't handled.
1575  */
1576 static int
1577 send_data(FILE *instr, FILE *outstr, off_t blksize, int isdata)
1578 {
1579         int      c, filefd, netfd, rval;
1580         char    *buf;
1581
1582         transflag = 1;
1583         rval = -1;
1584         buf = NULL;
1585         if (setjmp(urgcatch))
1586                 goto cleanup_send_data;
1587
1588         switch (type) {
1589
1590         case TYPE_A:
1591  /* XXXLUKEM: rate limit ascii send (get) */
1592                 (void) alarm(curclass.timeout);
1593                 while ((c = getc(instr)) != EOF) {
1594                         byte_count++;
1595                         if (c == '\n') {
1596                                 if (ferror(outstr))
1597                                         goto data_err;
1598                                 (void) putc('\r', outstr);
1599                                 if (isdata) {
1600                                         total_data_out++;
1601                                         total_data++;
1602                                 }
1603                                 total_bytes_out++;
1604                                 total_bytes++;
1605                         }
1606                         (void) putc(c, outstr);
1607                         if (isdata) {
1608                                 total_data_out++;
1609                                 total_data++;
1610                         }
1611                         total_bytes_out++;
1612                         total_bytes++;
1613                         if ((byte_count % 4096) == 0)
1614                                 (void) alarm(curclass.timeout);
1615                 }
1616                 (void) alarm(0);
1617                 fflush(outstr);
1618                 if (ferror(instr))
1619                         goto file_err;
1620                 if (ferror(outstr))
1621                         goto data_err;
1622                 rval = 0;
1623                 goto cleanup_send_data;
1624
1625         case TYPE_I:
1626         case TYPE_L:
1627                 if ((buf = malloc((size_t)blksize)) == NULL) {
1628                         perror_reply(451, "Local resource failure: malloc");
1629                         goto cleanup_send_data;
1630                 }
1631                 filefd = fileno(instr);
1632                 netfd = fileno(outstr);
1633                 (void) alarm(curclass.timeout);
1634                 if (curclass.rateget) {
1635                         while (1) {
1636                                 int d;
1637                                 struct timeval then, now, td;
1638                                 off_t bufrem;
1639                                 char *bufp;
1640
1641                                 (void)gettimeofday(&then, NULL);
1642                                 errno = c = d = 0;
1643                                 bufrem = curclass.rateget;
1644                                 while (bufrem > 0) {
1645                                         if ((c = read(filefd, buf,
1646                                             MIN(blksize, bufrem))) <= 0)
1647                                                 goto senddone;
1648                                         (void) alarm(curclass.timeout);
1649                                         bufrem -= c;
1650                                         byte_count += c;
1651                                         if (isdata) {
1652                                                 total_data_out += c;
1653                                                 total_data += c;
1654                                         }
1655                                         total_bytes_out += c;
1656                                         total_bytes += c;
1657                                         for (bufp = buf; c > 0;
1658                                             c -= d, bufp += d)
1659                                                 if ((d =
1660                                                     write(netfd, bufp, c)) <= 0)
1661                                                         break;
1662                                         if (d < 0)
1663                                                 goto data_err;
1664                                 }
1665                                 (void)gettimeofday(&now, NULL);
1666                                 timersub(&now, &then, &td);
1667                                 if (td.tv_sec == 0)
1668                                         usleep(1000000 - td.tv_usec);
1669                         }
1670                 } else {
1671                         while ((c = read(filefd, buf, (size_t)blksize)) > 0) {
1672                                 if (write(netfd, buf, c) != c)
1673                                         goto data_err;
1674                                 (void) alarm(curclass.timeout);
1675                                 byte_count += c;
1676                                 if (isdata) {
1677                                         total_data_out += c;
1678                                         total_data += c;
1679                                 }
1680                                 total_bytes_out += c;
1681                                 total_bytes += c;
1682                         }
1683                 }
1684  senddone:
1685                 if (c < 0)
1686                         goto file_err;
1687                 rval = 0;
1688                 goto cleanup_send_data;
1689
1690         default:
1691                 reply(550, "Unimplemented TYPE %d in send_data", type);
1692                 goto cleanup_send_data;
1693         }
1694
1695  data_err:
1696         (void) alarm(0);
1697         perror_reply(426, "Data connection");
1698         goto cleanup_send_data;
1699
1700  file_err:
1701         (void) alarm(0);
1702         perror_reply(551, "Error on input file");
1703                 /* FALLTHROUGH */
1704
1705  cleanup_send_data:
1706         (void) alarm(0);
1707         transflag = 0;
1708         if (buf)
1709                 free(buf);
1710         if (isdata) {
1711                 total_files_out++;
1712                 total_files++;
1713         }
1714         total_xfers_out++;
1715         total_xfers++;
1716         return (rval);
1717 }
1718
1719 /*
1720  * Transfer data from peer to "outstr" using the appropriate encapulation of
1721  * the data subject to Mode, Structure, and Type.
1722  *
1723  * N.B.: Form isn't handled.
1724  */
1725 static int
1726 receive_data(FILE *instr, FILE *outstr)
1727 {
1728         int     c, bare_lfs, netfd, filefd, rval;
1729         off_t   byteswritten;
1730         char    buf[BUFSIZ];
1731 #ifdef __GNUC__
1732         (void) &bare_lfs;
1733 #endif
1734
1735         bare_lfs = 0;
1736         transflag = 1;
1737         rval = -1;
1738         byteswritten = 0;
1739         if (setjmp(urgcatch))
1740                 goto cleanup_recv_data;
1741
1742 #define FILESIZECHECK(x) \
1743                         do { \
1744                                 if (curclass.maxfilesize != -1 && \
1745                                     (x) > curclass.maxfilesize) { \
1746                                         errno = EFBIG; \
1747                                         goto file_err; \
1748                                 } \
1749                         } while (0)
1750
1751         switch (type) {
1752
1753         case TYPE_I:
1754         case TYPE_L:
1755                 netfd = fileno(instr);
1756                 filefd = fileno(outstr);
1757                 (void) alarm(curclass.timeout);
1758                 if (curclass.rateput) {
1759                         while (1) {
1760                                 int d;
1761                                 struct timeval then, now, td;
1762                                 off_t bufrem;
1763
1764                                 (void)gettimeofday(&then, NULL);
1765                                 errno = c = d = 0;
1766                                 for (bufrem = curclass.rateput; bufrem > 0; ) {
1767                                         if ((c = read(netfd, buf,
1768                                             MIN(sizeof(buf), bufrem))) <= 0)
1769                                                 goto recvdone;
1770                                         FILESIZECHECK(byte_count + c);
1771                                         if ((d = write(filefd, buf, c)) != c)
1772                                                 goto file_err;
1773                                         (void) alarm(curclass.timeout);
1774                                         bufrem -= c;
1775                                         byte_count += c;
1776                                         total_data_in += c;
1777                                         total_data += c;
1778                                         total_bytes_in += c;
1779                                         total_bytes += c;
1780                                 }
1781                                 (void)gettimeofday(&now, NULL);
1782                                 timersub(&now, &then, &td);
1783                                 if (td.tv_sec == 0)
1784                                         usleep(1000000 - td.tv_usec);
1785                         }
1786                 } else {
1787                         while ((c = read(netfd, buf, sizeof(buf))) > 0) {
1788                                 FILESIZECHECK(byte_count + c);
1789                                 if (write(filefd, buf, c) != c)
1790                                         goto file_err;
1791                                 (void) alarm(curclass.timeout);
1792                                 byte_count += c;
1793                                 total_data_in += c;
1794                                 total_data += c;
1795                                 total_bytes_in += c;
1796                                 total_bytes += c;
1797                         }
1798                 }
1799  recvdone:
1800                 if (c < 0)
1801                         goto data_err;
1802                 rval = 0;
1803                 goto cleanup_recv_data;
1804
1805         case TYPE_E:
1806                 reply(553, "TYPE E not implemented.");
1807                 goto cleanup_recv_data;
1808
1809         case TYPE_A:
1810                 (void) alarm(curclass.timeout);
1811  /* XXXLUKEM: rate limit ascii receive (put) */
1812                 while ((c = getc(instr)) != EOF) {
1813                         byte_count++;
1814                         total_data_in++;
1815                         total_data++;
1816                         total_bytes_in++;
1817                         total_bytes++;
1818                         if ((byte_count % 4096) == 0)
1819                                 (void) alarm(curclass.timeout);
1820                         if (c == '\n')
1821                                 bare_lfs++;
1822                         while (c == '\r') {
1823                                 if (ferror(outstr))
1824                                         goto data_err;
1825                                 if ((c = getc(instr)) != '\n') {
1826                                         byte_count++;
1827                                         total_data_in++;
1828                                         total_data++;
1829                                         total_bytes_in++;
1830                                         total_bytes++;
1831                                         if ((byte_count % 4096) == 0)
1832                                                 (void) alarm(curclass.timeout);
1833                                         byteswritten++;
1834                                         FILESIZECHECK(byteswritten);
1835                                         (void) putc ('\r', outstr);
1836                                         if (c == '\0' || c == EOF)
1837                                                 goto contin2;
1838                                 }
1839                         }
1840                         byteswritten++;
1841                         FILESIZECHECK(byteswritten);
1842                         (void) putc(c, outstr);
1843  contin2:       ;
1844                 }
1845                 (void) alarm(0);
1846                 fflush(outstr);
1847                 if (ferror(instr))
1848                         goto data_err;
1849                 if (ferror(outstr))
1850                         goto file_err;
1851                 if (bare_lfs) {
1852                         reply(-226,
1853                             "WARNING! %d bare linefeeds received in ASCII mode",
1854                             bare_lfs);
1855                         reply(0, "File may not have transferred correctly.");
1856                 }
1857                 rval = 0;
1858                 goto cleanup_recv_data;
1859
1860         default:
1861                 reply(550, "Unimplemented TYPE %d in receive_data", type);
1862                 goto cleanup_recv_data;
1863         }
1864 #undef FILESIZECHECK
1865
1866  data_err:
1867         (void) alarm(0);
1868         perror_reply(426, "Data Connection");
1869         goto cleanup_recv_data;
1870
1871  file_err:
1872         (void) alarm(0);
1873         perror_reply(452, "Error writing file");
1874         goto cleanup_recv_data;
1875
1876  cleanup_recv_data:
1877         (void) alarm(0);
1878         transflag = 0;
1879         total_files_in++;
1880         total_files++;
1881         total_xfers_in++;
1882         total_xfers++;
1883         return (rval);
1884 }
1885
1886 void
1887 statcmd(void)
1888 {
1889         struct sockinet *su = NULL;
1890         static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
1891         u_char *a, *p;
1892         int ispassive, af;
1893         off_t otbi, otbo, otb;
1894
1895         a = p = (u_char *)NULL;
1896
1897         reply(-211, "%s FTP server status:", hostname);
1898         reply(0, "Version: %s", EMPTYSTR(version) ? "<suppressed>" : version);
1899         hbuf[0] = '\0';
1900         if (!getnameinfo((struct sockaddr *)&his_addr.si_su, his_addr.su_len,
1901                         hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)
1902             && strcmp(remotehost, hbuf) != 0)
1903                 reply(0, "Connected to %s (%s)", remotehost, hbuf);
1904         else
1905                 reply(0, "Connected to %s", remotehost);
1906
1907         if (logged_in) {
1908                 if (curclass.type == CLASS_GUEST)
1909                         reply(0, "Logged in anonymously");
1910                 else
1911                         reply(0, "Logged in as %s%s", pw->pw_name,
1912                             curclass.type == CLASS_CHROOT ? " (chroot)" : "");
1913         } else if (askpasswd)
1914                 reply(0, "Waiting for password");
1915         else
1916                 reply(0, "Waiting for user name");
1917         cprintf(stdout, "    TYPE: %s", typenames[type]);
1918         if (type == TYPE_A || type == TYPE_E)
1919                 cprintf(stdout, ", FORM: %s", formnames[form]);
1920         if (type == TYPE_L) {
1921 #if NBBY == 8
1922                 cprintf(stdout, " %d", NBBY);
1923 #else
1924                         /* XXX: `bytesize' needs to be defined in this case */
1925                 cprintf(stdout, " %d", bytesize);
1926 #endif
1927         }
1928         cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
1929             strunames[stru], modenames[mode]);
1930         ispassive = 0;
1931         if (data != -1) {
1932                 reply(0, "Data connection open");
1933                 su = NULL;
1934         } else if (pdata != -1) {
1935                 reply(0, "in Passive mode");
1936                 if (curclass.advertise.su_len != 0)
1937                         su = &curclass.advertise;
1938                 else
1939                         su = &pasv_addr;
1940                 ispassive = 1;
1941                 goto printaddr;
1942         } else if (usedefault == 0) {
1943                 if (epsvall) {
1944                         reply(0, "EPSV only mode (EPSV ALL)");
1945                         goto epsvonly;
1946                 }
1947                 su = (struct sockinet *)&data_dest;
1948  printaddr:
1949                                                         /* PASV/PORT */
1950                 if (su->su_family == AF_INET) {
1951                         a = (u_char *) &su->su_addr;
1952                         p = (u_char *) &su->su_port;
1953 #define UC(b) (((int) b) & 0xff)
1954                         reply(0, "%s (%d,%d,%d,%d,%d,%d)",
1955                                 ispassive ? "PASV" : "PORT" ,
1956                                 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1957                                 UC(p[0]), UC(p[1]));
1958                 }
1959
1960                                                         /* LPSV/LPRT */
1961             {
1962                 int alen, i;
1963
1964                 alen = 0;
1965                 switch (su->su_family) {
1966                 case AF_INET:
1967                         a = (u_char *) &su->su_addr;
1968                         p = (u_char *) &su->su_port;
1969                         alen = sizeof(su->su_addr);
1970                         af = 4;
1971                         break;
1972 #ifdef INET6
1973                 case AF_INET6:
1974                         a = (u_char *) &su->su_6addr;
1975                         p = (u_char *) &su->su_port;
1976                         alen = sizeof(su->su_6addr);
1977                         af = 6;
1978                         break;
1979 #endif
1980                 default:
1981                         af = 0;
1982                         break;
1983                 }
1984                 if (af) {
1985                         cprintf(stdout, "    %s (%d,%d",
1986                             ispassive ? "LPSV" : "LPRT", af, alen);
1987                         for (i = 0; i < alen; i++)
1988                                 cprintf(stdout, ",%d", UC(a[i]));
1989                         cprintf(stdout, ",%d,%d,%d)\r\n",
1990                             2, UC(p[0]), UC(p[1]));
1991 #undef UC
1992                 }
1993             }
1994
1995                 /* EPRT/EPSV */
1996  epsvonly:
1997                 af = af2epsvproto(su->su_family);
1998                 hbuf[0] = '\0';
1999                 if (af > 0) {
2000                         struct sockinet tmp;
2001
2002                         tmp = *su;
2003 #ifdef INET6
2004                         if (tmp.su_family == AF_INET6)
2005                                 tmp.su_scope_id = 0;
2006 #endif
2007                         if (getnameinfo((struct sockaddr *)&tmp.si_su,
2008                             tmp.su_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
2009                             NI_NUMERICHOST | NI_NUMERICSERV) == 0)
2010                                 reply(0, "%s (|%d|%s|%s|)",
2011                                     ispassive ? "EPSV" : "EPRT",
2012                                     af, hbuf, sbuf);
2013                 }
2014         } else
2015                 reply(0, "No data connection");
2016
2017         if (logged_in) {
2018                 reply(0,
2019                     "Data sent:        " LLF " byte%s in " LLF " file%s",
2020                     (LLT)total_data_out, PLURAL(total_data_out),
2021                     (LLT)total_files_out, PLURAL(total_files_out));
2022                 reply(0,
2023                     "Data received:    " LLF " byte%s in " LLF " file%s",
2024                     (LLT)total_data_in, PLURAL(total_data_in),
2025                     (LLT)total_files_in, PLURAL(total_files_in));
2026                 reply(0,
2027                     "Total data:       " LLF " byte%s in " LLF " file%s",
2028                     (LLT)total_data, PLURAL(total_data),
2029                     (LLT)total_files, PLURAL(total_files));
2030         }
2031         otbi = total_bytes_in;
2032         otbo = total_bytes_out;
2033         otb = total_bytes;
2034         reply(0, "Traffic sent:     " LLF " byte%s in " LLF " transfer%s",
2035             (LLT)otbo, PLURAL(otbo),
2036             (LLT)total_xfers_out, PLURAL(total_xfers_out));
2037         reply(0, "Traffic received: " LLF " byte%s in " LLF " transfer%s",
2038             (LLT)otbi, PLURAL(otbi),
2039             (LLT)total_xfers_in, PLURAL(total_xfers_in));
2040         reply(0, "Total traffic:    " LLF " byte%s in " LLF " transfer%s",
2041             (LLT)otb, PLURAL(otb),
2042             (LLT)total_xfers, PLURAL(total_xfers));
2043
2044         if (logged_in && !CURCLASS_FLAGS_ISSET(private)) {
2045                 struct ftpconv *cp;
2046
2047                 reply(0, "%s", "");
2048                 reply(0, "Class: %s, type: %s",
2049                     curclass.classname, CURCLASSTYPE);
2050                 reply(0, "Check PORT/LPRT commands: %sabled",
2051                     CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
2052                 if (! EMPTYSTR(curclass.display))
2053                         reply(0, "Display file: %s", curclass.display);
2054                 if (! EMPTYSTR(curclass.notify))
2055                         reply(0, "Notify fileglob: %s", curclass.notify);
2056                 reply(0, "Idle timeout: %d, maximum timeout: %d",
2057                     curclass.timeout, curclass.maxtimeout);
2058                 reply(0, "Current connections: %d", connections);
2059                 if (curclass.limit == -1)
2060                         reply(0, "Maximum connections: unlimited");
2061                 else
2062                         reply(0, "Maximum connections: %d", curclass.limit);
2063                 if (curclass.limitfile)
2064                         reply(0, "Connection limit exceeded message file: %s",
2065                             conffilename(curclass.limitfile));
2066                 if (! EMPTYSTR(curclass.chroot))
2067                         reply(0, "Chroot format: %s", curclass.chroot);
2068                 reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2069                     CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis");
2070                 if (! EMPTYSTR(curclass.homedir))
2071                         reply(0, "Homedir format: %s", curclass.homedir);
2072                 if (curclass.maxfilesize == -1)
2073                         reply(0, "Maximum file size: unlimited");
2074                 else
2075                         reply(0, "Maximum file size: " LLF,
2076                             (LLT)curclass.maxfilesize);
2077                 if (! EMPTYSTR(curclass.motd))
2078                         reply(0, "MotD file: %s", conffilename(curclass.motd));
2079                 reply(0,
2080             "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2081                     CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis");
2082                 reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2083                     CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis");
2084                 reply(0, "Sanitize file names: %sabled",
2085                     CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis");
2086                 reply(0, "PASV/LPSV/EPSV connections: %sabled",
2087                     CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis");
2088                 if (curclass.advertise.su_len != 0) {
2089                         char buf[50];   /* big enough for IPv6 address */
2090                         const char *bp;
2091
2092                         bp = inet_ntop(curclass.advertise.su_family,
2093                             (void *)&curclass.advertise.su_addr,
2094                             buf, sizeof(buf));
2095                         if (bp != NULL)
2096                                 reply(0, "PASV advertise address: %s", bp);
2097                 }
2098                 if (curclass.portmin && curclass.portmax)
2099                         reply(0, "PASV port range: %d - %d",
2100                             curclass.portmin, curclass.portmax);
2101                 if (curclass.rateget)
2102                         reply(0, "Rate get limit: " LLF " bytes/sec",
2103                             (LLT)curclass.rateget);
2104                 else
2105                         reply(0, "Rate get limit: disabled");
2106                 if (curclass.rateput)
2107                         reply(0, "Rate put limit: " LLF " bytes/sec",
2108                             (LLT)curclass.rateput);
2109                 else
2110                         reply(0, "Rate put limit: disabled");
2111                 reply(0, "Umask: %.04o", curclass.umask);
2112                 for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
2113                         if (cp->suffix == NULL || cp->types == NULL ||
2114                             cp->command == NULL)
2115                                 continue;
2116                         reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2117                             cp->suffix, cp->types, cp->disable, cp->command);
2118                 }
2119         }
2120
2121         reply(211, "End of status");
2122 }
2123
2124 void
2125 fatal(const char *s)
2126 {
2127
2128         reply(451, "Error in server: %s\n", s);
2129         reply(221, "Closing connection due to server error.");
2130         dologout(0);
2131         /* NOTREACHED */
2132 }
2133
2134 /*
2135  * reply() --
2136  *      depending on the value of n, display fmt with a trailing CRLF and
2137  *      prefix of:
2138  *      n < -1          prefix the message with abs(n) + "-"    (initial line)
2139  *      n == 0          prefix the message with 4 spaces        (middle lines)
2140  *      n >  0          prefix the message with n + " "         (final line)
2141  */
2142 void
2143 reply(int n, const char *fmt, ...)
2144 {
2145         off_t b;
2146         va_list ap;
2147
2148         va_start(ap, fmt);
2149         b = 0;
2150         if (n == 0)
2151                 cprintf(stdout, "    ");
2152         else if (n < 0)
2153                 cprintf(stdout, "%d-", -n);
2154         else
2155                 cprintf(stdout, "%d ", n);
2156         b = vprintf(fmt, ap);
2157         va_end(ap);
2158         total_bytes += b;
2159         total_bytes_out += b;
2160         cprintf(stdout, "\r\n");
2161         (void)fflush(stdout);
2162         if (debug) {
2163                 syslog(LOG_DEBUG, "<--- %d%c", abs(n), (n < 0) ? '-' : ' ');
2164                 va_start(ap, fmt);
2165                 vsyslog(LOG_DEBUG, fmt, ap);
2166                 va_end(ap);
2167         }
2168 }
2169
2170 static void
2171 logremotehost(struct sockinet *who)
2172 {
2173
2174         if (getnameinfo((struct sockaddr *)&who->si_su,
2175             who->su_len, remotehost, sizeof(remotehost), NULL, 0, 0))
2176                 strlcpy(remotehost, "?", sizeof(remotehost));
2177
2178 #if HAVE_SETPROCTITLE
2179         snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
2180         setproctitle("%s", proctitle);
2181 #endif /* HAVE_SETPROCTITLE */
2182         if (logging)
2183                 syslog(LOG_INFO, "connection from %s to %s",
2184                     remotehost, hostname);
2185 }
2186
2187 /*
2188  * Record logout in wtmp file and exit with supplied status.
2189  */
2190 void
2191 dologout(int status)
2192 {
2193         /*
2194         * Prevent reception of SIGURG from resulting in a resumption
2195         * back to the main program loop.
2196         */
2197         transflag = 0;
2198
2199         if (logged_in) {
2200 #ifdef NO_UTMP
2201                 if (dowtmp)
2202                         logwtmp(ttyline, "", "");
2203                 if (doutmp)
2204                         logout(utmp.ut_line);
2205 #endif /* NO_UTMP */
2206 #ifdef KERBEROS
2207                 if (!notickets && krbtkfile_env)
2208                         unlink(krbtkfile_env);
2209 #endif
2210         }
2211         /* beware of flushing buffers after a SIGPIPE */
2212         _exit(status);
2213 }
2214
2215 void
2216 abor(void)
2217 {
2218
2219         tmpline[0] = '\0';
2220         is_oob = 0;
2221         reply(426, "Transfer aborted. Data connection closed.");
2222         reply(226, "Abort successful");
2223         longjmp(urgcatch, 1);
2224 }
2225
2226 void
2227 statxfer(void)
2228 {
2229
2230         tmpline[0] = '\0';
2231         is_oob = 0;
2232         if (file_size != (off_t) -1)
2233                 reply(213,
2234                     "Status: " LLF " of " LLF " byte%s transferred",
2235                     (LLT)byte_count, (LLT)file_size,
2236                     PLURAL(byte_count));
2237         else
2238                 reply(213, "Status: " LLF " byte%s transferred",
2239                     (LLT)byte_count, PLURAL(byte_count));
2240 }
2241
2242 static void
2243 myoob(int signo)
2244 {
2245         char *cp;
2246
2247         /* only process if transfer occurring */
2248         if (!transflag)
2249                 return;
2250         cp = tmpline;
2251         if (getline(cp, sizeof(tmpline), stdin) == NULL) {
2252                 reply(221, "You could at least say goodbye.");
2253                 dologout(0);
2254         }
2255         is_oob = 1;
2256         ftp_handle_line(cp);
2257         is_oob = 0;
2258 }
2259
2260 static int
2261 bind_pasv_addr(void)
2262 {
2263         static int passiveport;
2264         int port, len;
2265
2266         len = pasv_addr.su_len;
2267         if (curclass.portmin == 0 && curclass.portmax == 0) {
2268                 pasv_addr.su_port = 0;
2269                 return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len));
2270         }
2271
2272         if (passiveport == 0) {
2273                 srand(getpid());
2274                 passiveport = rand() % (curclass.portmax - curclass.portmin)
2275                     + curclass.portmin;
2276         }
2277
2278         port = passiveport;
2279         while (1) {
2280                 port++;
2281                 if (port > curclass.portmax)
2282                         port = curclass.portmin;
2283                 else if (port == passiveport) {
2284                         errno = EAGAIN;
2285                         return (-1);
2286                 }
2287                 pasv_addr.su_port = htons(port);
2288                 if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0)
2289                         break;
2290                 if (errno != EADDRINUSE)
2291                         return (-1);
2292         }
2293         passiveport = port;
2294         return (0);
2295 }
2296
2297 /*
2298  * Note: a response of 425 is not mentioned as a possible response to
2299  *      the PASV command in RFC959. However, it has been blessed as
2300  *      a legitimate response by Jon Postel in a telephone conversation
2301  *      with Rick Adams on 25 Jan 89.
2302  */
2303 void
2304 passive(void)
2305 {
2306         int len;
2307         char *p, *a;
2308
2309         if (pdata >= 0)
2310                 close(pdata);
2311         pdata = socket(AF_INET, SOCK_STREAM, 0);
2312         if (pdata < 0 || !logged_in) {
2313                 perror_reply(425, "Can't open passive connection");
2314                 return;
2315         }
2316         pasv_addr = ctrl_addr;
2317
2318         if (bind_pasv_addr() < 0)
2319                 goto pasv_error;
2320         len = pasv_addr.su_len;
2321         if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
2322                 goto pasv_error;
2323         pasv_addr.su_len = len;
2324         if (listen(pdata, 1) < 0)
2325                 goto pasv_error;
2326         if (curclass.advertise.su_len != 0)
2327                 a = (char *) &curclass.advertise.su_addr;
2328         else
2329                 a = (char *) &pasv_addr.su_addr;
2330         p = (char *) &pasv_addr.su_port;
2331
2332 #define UC(b) (((int) b) & 0xff)
2333
2334         reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
2335                 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
2336         return;
2337
2338  pasv_error:
2339         (void) close(pdata);
2340         pdata = -1;
2341         perror_reply(425, "Can't open passive connection");
2342         return;
2343 }
2344
2345 /*
2346  * convert protocol identifier to/from AF
2347  */
2348 int
2349 lpsvproto2af(int proto)
2350 {
2351
2352         switch (proto) {
2353         case 4:
2354                 return AF_INET;
2355 #ifdef INET6
2356         case 6:
2357                 return AF_INET6;
2358 #endif
2359         default:
2360                 return -1;
2361         }
2362 }
2363
2364 int
2365 af2lpsvproto(int af)
2366 {
2367
2368         switch (af) {
2369         case AF_INET:
2370                 return 4;
2371 #ifdef INET6
2372         case AF_INET6:
2373                 return 6;
2374 #endif
2375         default:
2376                 return -1;
2377         }
2378 }
2379
2380 int
2381 epsvproto2af(int proto)
2382 {
2383
2384         switch (proto) {
2385         case 1:
2386                 return AF_INET;
2387 #ifdef INET6
2388         case 2:
2389                 return AF_INET6;
2390 #endif
2391         default:
2392                 return -1;
2393         }
2394 }
2395
2396 int
2397 af2epsvproto(int af)
2398 {
2399
2400         switch (af) {
2401         case AF_INET:
2402                 return 1;
2403 #ifdef INET6
2404         case AF_INET6:
2405                 return 2;
2406 #endif
2407         default:
2408                 return -1;
2409         }
2410 }
2411
2412 /*
2413  * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
2414  * 229 Entering Extended Passive Mode (|||port|)
2415  */
2416 void
2417 long_passive(char *cmd, int pf)
2418 {
2419         int len;
2420         char *p, *a;
2421
2422         if (!logged_in) {
2423                 syslog(LOG_NOTICE, "long passive but not logged in");
2424                 reply(503, "Login with USER first.");
2425                 return;
2426         }
2427
2428         if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
2429                 /*
2430                  * XXX: only EPRT/EPSV ready clients will understand this
2431                  */
2432                 if (strcmp(cmd, "EPSV") != 0)
2433                         reply(501, "Network protocol mismatch"); /*XXX*/
2434                 else
2435                         epsv_protounsupp("Network protocol mismatch");
2436
2437                 return;
2438         }
2439  
2440         if (pdata >= 0)
2441                 close(pdata);
2442         pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
2443         if (pdata < 0) {
2444                 perror_reply(425, "Can't open passive connection");
2445                 return;
2446         }
2447         pasv_addr = ctrl_addr;
2448         if (bind_pasv_addr() < 0)
2449                 goto pasv_error;
2450         len = pasv_addr.su_len;
2451         if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
2452                 goto pasv_error;
2453         pasv_addr.su_len = len;
2454         if (listen(pdata, 1) < 0)
2455                 goto pasv_error;
2456         p = (char *) &pasv_addr.su_port;
2457
2458 #define UC(b) (((int) b) & 0xff)
2459
2460         if (strcmp(cmd, "LPSV") == 0) {
2461                 struct sockinet *advert;
2462
2463                 if (curclass.advertise.su_len != 0)
2464                         advert = &curclass.advertise;
2465                 else
2466                         advert = &pasv_addr;
2467                 switch (advert->su_family) {
2468                 case AF_INET:
2469                         a = (char *) &advert->su_addr;
2470                         reply(228,
2471     "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2472                                 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2473                                 2, UC(p[0]), UC(p[1]));
2474                         return;
2475 #ifdef INET6
2476                 case AF_INET6:
2477                         a = (char *) &advert->su_6addr;
2478                         reply(228,
2479     "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2480                                 6, 16,
2481                                 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2482                                 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
2483                                 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
2484                                 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
2485                                 2, UC(p[0]), UC(p[1]));
2486                         return;
2487 #endif
2488                 }
2489 #undef UC
2490         } else if (strcmp(cmd, "EPSV") == 0) {
2491                 switch (pasv_addr.su_family) {
2492                 case AF_INET:
2493 #ifdef INET6
2494                 case AF_INET6:
2495 #endif
2496                         reply(229, "Entering Extended Passive Mode (|||%d|)",
2497                             ntohs(pasv_addr.su_port));
2498                         return;
2499                 }
2500         } else {
2501                 /* more proper error code? */
2502         }
2503
2504  pasv_error:
2505         (void) close(pdata);
2506         pdata = -1;
2507         perror_reply(425, "Can't open passive connection");
2508         return;
2509 }
2510
2511 int
2512 extended_port(const char *arg)
2513 {
2514         char *tmp = NULL;
2515         char *result[3];
2516         char *p, *q;
2517         char delim;
2518         struct addrinfo hints;
2519         struct addrinfo *res = NULL;
2520         int i;
2521         unsigned long proto;
2522
2523         tmp = xstrdup(arg);
2524         p = tmp;
2525         delim = p[0];
2526         p++;
2527         memset(result, 0, sizeof(result));
2528         for (i = 0; i < 3; i++) {
2529                 q = strchr(p, delim);
2530                 if (!q || *q != delim)
2531                         goto parsefail;
2532                 *q++ = '\0';
2533                 result[i] = p;
2534                 p = q;
2535         }
2536
2537                         /* some more sanity checks */
2538         p = NULL;
2539         (void)strtoul(result[2], &p, 10);
2540         if (!*result[2] || *p)
2541                 goto parsefail;
2542         p = NULL;
2543         proto = strtoul(result[0], &p, 10);
2544         if (!*result[0] || *p)
2545                 goto protounsupp;
2546          
2547         memset(&hints, 0, sizeof(hints));
2548         hints.ai_family = epsvproto2af((int)proto);
2549         if (hints.ai_family < 0)
2550                 goto protounsupp;
2551         hints.ai_socktype = SOCK_STREAM;
2552         hints.ai_flags = AI_NUMERICHOST;
2553         if (getaddrinfo(result[1], result[2], &hints, &res))
2554                 goto parsefail;
2555         if (res->ai_next)
2556                 goto parsefail;
2557         if (sizeof(data_dest) < res->ai_addrlen)
2558                 goto parsefail;
2559         memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen);
2560         data_dest.su_len = res->ai_addrlen;
2561 #ifdef INET6
2562         if (his_addr.su_family == AF_INET6 &&
2563             data_dest.su_family == AF_INET6) {
2564                         /* XXX: more sanity checks! */
2565                 data_dest.su_scope_id = his_addr.su_scope_id;
2566         }
2567 #endif
2568
2569         if (tmp != NULL)
2570                 free(tmp);
2571         if (res)
2572                 freeaddrinfo(res);
2573         return 0;
2574
2575  parsefail:
2576         reply(500, "Invalid argument, rejected.");
2577         usedefault = 1;
2578         if (tmp != NULL)
2579                 free(tmp);
2580         if (res)
2581                 freeaddrinfo(res);
2582         return -1;
2583
2584  protounsupp:
2585         epsv_protounsupp("Protocol not supported");
2586         usedefault = 1;
2587         if (tmp != NULL)
2588                 free(tmp);
2589         if (res)
2590                 freeaddrinfo(res);
2591         return -1;
2592 }
2593
2594 /*
2595  * 522 Protocol not supported (proto,...)
2596  * as we assume address family for control and data connections are the same,
2597  * we do not return the list of address families we support - instead, we
2598  * return the address family of the control connection.
2599  */
2600 void
2601 epsv_protounsupp(const char *message)
2602 {
2603         int proto;
2604
2605         proto = af2epsvproto(ctrl_addr.su_family);
2606         if (proto < 0)
2607                 reply(501, "%s", message);      /* XXX */
2608         else
2609                 reply(522, "%s, use (%d)", message, proto);
2610 }
2611
2612 /*
2613  * Generate unique name for file with basename "local".
2614  * The file named "local" is already known to exist.
2615  * Generates failure reply on error.
2616  *
2617  * XXX: this function should under go changes similar to
2618  *      the mktemp(3)/mkstemp(3) changes.
2619  */
2620 static char *
2621 gunique(const char *local)
2622 {
2623         static char new[MAXPATHLEN];
2624         struct stat st;
2625         char *cp;
2626         int count;
2627
2628         cp = strrchr(local, '/');
2629         if (cp)
2630                 *cp = '\0';
2631         if (stat(cp ? local : ".", &st) < 0) {
2632                 perror_reply(553, cp ? local : ".");
2633                 return (NULL);
2634         }
2635         if (cp)
2636                 *cp = '/';
2637         for (count = 1; count < 100; count++) {
2638                 (void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count);
2639                 if (stat(new, &st) < 0)
2640                         return (new);
2641         }
2642         reply(452, "Unique file name cannot be created.");
2643         return (NULL);
2644 }
2645
2646 /*
2647  * Format and send reply containing system error number.
2648  */
2649 void
2650 perror_reply(int code, const char *string)
2651 {
2652         int save_errno;
2653
2654         save_errno = errno;
2655         reply(code, "%s: %s.", string, strerror(errno));
2656         errno = save_errno;
2657 }
2658
2659 static char *onefile[] = {
2660         "",
2661         0
2662 };
2663
2664 void
2665 send_file_list(const char *whichf)
2666 {
2667         struct stat st;
2668         DIR *dirp = NULL;
2669         struct dirent *dir;
2670         FILE *dout = NULL;
2671         char **dirlist, *dirname, *notglob, *p;
2672         int simple = 0;
2673         int freeglob = 0;
2674         glob_t gl;
2675
2676 #ifdef __GNUC__
2677         (void) &dout;
2678         (void) &dirlist;
2679         (void) &simple;
2680         (void) &freeglob;
2681 #endif
2682
2683         p = NULL;
2684         if (strpbrk(whichf, "~{[*?") != NULL) {
2685                 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
2686
2687                 memset(&gl, 0, sizeof(gl));
2688                 freeglob = 1;
2689                 if (glob(whichf, flags, 0, &gl)) {
2690                         reply(550, "not found");
2691                         goto out;
2692                 } else if (gl.gl_pathc == 0) {
2693                         errno = ENOENT;
2694                         perror_reply(550, whichf);
2695                         goto out;
2696                 }
2697                 dirlist = gl.gl_pathv;
2698         } else {
2699                 notglob = xstrdup(whichf);
2700                 onefile[0] = notglob;
2701                 dirlist = onefile;
2702                 simple = 1;
2703         }
2704                                         /* XXX: } for vi sm */
2705
2706         if (setjmp(urgcatch)) {
2707                 transflag = 0;
2708                 goto out;
2709         }
2710         while ((dirname = *dirlist++) != NULL) {
2711                 int trailingslash = 0;
2712
2713                 if (stat(dirname, &st) < 0) {
2714                         /*
2715                          * If user typed "ls -l", etc, and the client
2716                          * used NLST, do what the user meant.
2717                          */
2718                         /* XXX: nuke this support? */
2719                         if (dirname[0] == '-' && *dirlist == NULL &&
2720                             transflag == 0) {
2721                                 char *argv[] = { INTERNAL_LS, "", NULL };
2722
2723                                 argv[1] = dirname;
2724                                 retrieve(argv, dirname);
2725                                 goto out;
2726                         }
2727                         perror_reply(550, whichf);
2728                         goto cleanup_send_file_list;
2729                 }
2730
2731                 if (S_ISREG(st.st_mode)) {
2732                         /*
2733                          * XXXRFC:
2734                          *      should we follow RFC959 and not work
2735                          *      for non directories?
2736                          */
2737                         if (dout == NULL) {
2738                                 dout = dataconn("file list", (off_t)-1, "w");
2739                                 if (dout == NULL)
2740                                         goto out;
2741                                 transflag++;
2742                         }
2743                         cprintf(dout, "%s%s\n", dirname,
2744                             type == TYPE_A ? "\r" : "");
2745                         continue;
2746                 } else if (!S_ISDIR(st.st_mode))
2747                         continue;
2748
2749                 if (dirname[strlen(dirname) - 1] == '/')
2750                         trailingslash++;
2751
2752                 if ((dirp = opendir(dirname)) == NULL)
2753                         continue;
2754
2755                 while ((dir = readdir(dirp)) != NULL) {
2756                         char nbuf[MAXPATHLEN];
2757
2758                         if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
2759                                 continue;
2760
2761                         (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
2762                             trailingslash ? "" : "/", dir->d_name);
2763
2764                         /*
2765                          * We have to do a stat to ensure it's
2766                          * not a directory or special file.
2767                          */
2768                         /*
2769                          * XXXRFC:
2770                          *      should we follow RFC959 and filter out
2771                          *      non files ?   lukem - NO!, or not until
2772                          *      our ftp client uses MLS{T,D} for completion.
2773                          */
2774                         if (simple || (stat(nbuf, &st) == 0 &&
2775                             S_ISREG(st.st_mode))) {
2776                                 if (dout == NULL) {
2777                                         dout = dataconn("file list", (off_t)-1,
2778                                                 "w");
2779                                         if (dout == NULL)
2780                                                 goto out;
2781                                         transflag++;
2782                                 }
2783                                 p = nbuf;
2784                                 if (nbuf[0] == '.' && nbuf[1] == '/')
2785                                         p = &nbuf[2];
2786                                 cprintf(dout, "%s%s\n", p,
2787                                     type == TYPE_A ? "\r" : "");
2788                         }
2789                 }
2790                 (void) closedir(dirp);
2791         }
2792
2793         if (dout == NULL)
2794                 reply(550, "No files found.");
2795         else if (ferror(dout) != 0)
2796                 perror_reply(550, "Data connection");
2797         else
2798                 reply(226, "Transfer complete.");
2799
2800  cleanup_send_file_list:
2801         transflag = 0;
2802         closedataconn(dout);
2803  out:
2804         total_xfers++;
2805         total_xfers_out++;
2806         if (notglob)
2807                 free(notglob);
2808         if (freeglob)
2809                 globfree(&gl);
2810 }
2811
2812 char *
2813 conffilename(const char *s)
2814 {
2815         static char filename[MAXPATHLEN];
2816
2817         if (*s == '/')
2818                 strlcpy(filename, s, sizeof(filename));
2819         else
2820                 (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
2821         return (filename);
2822 }
2823
2824 /*
2825  * logxfer --
2826  *      if logging > 1, then based on the arguments, syslog a message:
2827  *       if bytes != -1         "<command> <file1> = <bytes> bytes"
2828  *       else if file2 != NULL  "<command> <file1> <file2>"
2829  *       else                   "<command> <file1>"
2830  *      if elapsed != NULL, append "in xxx.yyy seconds"
2831  *      if error != NULL, append ": " + error
2832  *
2833  *      if doxferlog != 0, bytes != -1, and command is "get", "put",
2834  *      or "append", syslog a wu-ftpd style xferlog entry
2835  */
2836 void
2837 logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
2838         const struct timeval *elapsed, const char *error)
2839 {
2840         char             buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN];
2841         const char      *r1, *r2;
2842         char             direction;
2843         size_t           len;
2844         time_t           now;
2845
2846         if (logging <=1 && !doxferlog)
2847                 return;
2848
2849         r1 = r2 = NULL;
2850         if ((r1 = realpath(file1, realfile)) == NULL)
2851                 r1 = file1;
2852         if (file2 != NULL)
2853                 if ((r2 = realpath(file2, realfile)) == NULL)
2854                         r2 = file2;
2855
2856                 /*
2857                  * syslog command
2858                  */
2859         if (logging > 1) {
2860                 len = snprintf(buf, sizeof(buf), "%s %s", command, r1);
2861                 if (bytes != (off_t)-1)
2862                         len += snprintf(buf + len, sizeof(buf) - len,
2863                             " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes));
2864                 else if (r2 != NULL)
2865                         len += snprintf(buf + len, sizeof(buf) - len,
2866                             " %s", r2);
2867                 if (elapsed != NULL)
2868                         len += snprintf(buf + len, sizeof(buf) - len,
2869                             " in %ld.%.03d seconds", elapsed->tv_sec,
2870                             (int)(elapsed->tv_usec / 1000));
2871                 if (error != NULL)
2872                         len += snprintf(buf + len, sizeof(buf) - len,
2873                             ": %s", error);
2874                 syslog(LOG_INFO, "%s", buf);
2875         }
2876
2877
2878                 /*
2879                  * syslog wu-ftpd style log entry, prefixed with "xferlog: "
2880                  */
2881         if (!doxferlog || bytes == -1)
2882                 return;
2883
2884         if (strcmp(command, "get") == 0)
2885                 direction = 'o';
2886         else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0)
2887                 direction = 'i';
2888         else
2889                 return;
2890
2891         time(&now);
2892         syslog(LOG_INFO,
2893             "xferlog%s: %.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c",
2894
2895 /*
2896  * XXX: wu-ftpd puts (send) or (recv) in the syslog message, and removes
2897  *      the full date.  This may be problematic for accurate log parsing,
2898  *      given that syslog messages don't contain the full date.
2899  */
2900 #if 1           /* lukem's method; easier to convert to actual xferlog file */
2901             "",
2902             ctime(&now),
2903 #else           /* wu-ftpd's syslog method, with an extra unneeded space */
2904             (direction == 'i') ? " (recv)" : " (send)",
2905             "",
2906 #endif
2907             elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0),
2908             remotehost,
2909             (LLT) bytes,
2910             r1,
2911             type == TYPE_A ? 'a' : 'b',
2912             "_",                /* XXX: take conversions into account? */
2913             direction, 
2914
2915             curclass.type == CLASS_GUEST ?  'a' :
2916             curclass.type == CLASS_CHROOT ? 'g' :
2917             curclass.type == CLASS_REAL ?   'r' : '?',
2918
2919             curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
2920             error != NULL ? 'i' : 'c'
2921             );
2922 }
2923
2924 /*
2925  * Determine if `password' is valid for user given in `pw'.
2926  * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
2927  */
2928 int
2929 checkpassword(const struct passwd *pwent, const char *password)
2930 {
2931         char    *orig, *new;
2932         time_t   expire;
2933 #if HAVE_GETSPNAM
2934         struct spwd *spw;
2935 #endif
2936
2937         expire = 0;
2938         if (pwent == NULL)
2939                 return 1;
2940
2941 #if HAVE_GETSPNAM
2942         if ((spw = getspnam(pwent->pw_name)) == NULL)
2943                 return 1;
2944         orig = spw->sp_pwdp;
2945 #else
2946         orig = pwent->pw_passwd;        /* save existing password */
2947 #if HAVE_PW_EXPIRE
2948         expire = pwent->pw_expire;
2949 #endif
2950 #endif /* HAVE_GETSPNAM */
2951
2952         if (orig[0] == '\0')            /* don't allow empty passwords */
2953                 return 1;
2954
2955         new = crypt(password, orig);    /* encrypt given password */
2956         if (strcmp(new, orig) != 0)     /* compare */
2957                 return 1;
2958
2959         if (expire && time(NULL) >= expire)
2960                 return 2;               /* check if expired */
2961
2962         return 0;                       /* OK! */
2963 }
2964
2965 char *
2966 xstrdup(const char *s)
2967 {
2968         char *new = strdup(s);
2969
2970         if (new == NULL)
2971                 fatal("Local resource failure: malloc");
2972                 /* NOTREACHED */
2973         return (new);
2974 }
2975
2976 /*
2977  * As per fprintf(), but increment total_bytes and total_bytes_out,
2978  * by the appropriate amount.
2979  */
2980 void
2981 cprintf(FILE *fd, const char *fmt, ...)
2982 {
2983         off_t b;
2984         va_list ap;
2985
2986         va_start(ap, fmt);
2987         b = vfprintf(fd, fmt, ap);
2988         va_end(ap);
2989         total_bytes += b;
2990         total_bytes_out += b;
2991 }