1 /*        $NetBSD: ftpd.c,v 1.209 2025/03/26 00:05:56 christos Exp $  */
2 
3 /*
4  * Copyright (c) 1997-2023 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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
34  *        The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. Neither the name of the University nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  */
60 
61 /*
62  * Copyright (C) 1997 and 1998 WIDE Project.
63  * All rights reserved.
64  *
65  * Redistribution and use in source and binary forms, with or without
66  * modification, are permitted provided that the following conditions
67  * are met:
68  * 1. Redistributions of source code must retain the above copyright
69  *    notice, this list of conditions and the following disclaimer.
70  * 2. Redistributions in binary form must reproduce the above copyright
71  *    notice, this list of conditions and the following disclaimer in the
72  *    documentation and/or other materials provided with the distribution.
73  * 3. Neither the name of the project nor the names of its contributors
74  *    may be used to endorse or promote products derived from this software
75  *    without specific prior written permission.
76  *
77  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
78  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
81  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87  * SUCH DAMAGE.
88  */
89 
90 #include <sys/cdefs.h>
91 #ifndef lint
92 __COPYRIGHT("@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\
93  The Regents of the University of California.  All rights reserved.");
94 #endif /* not lint */
95 
96 #ifndef lint
97 #if 0
98 static char sccsid[] = "@(#)ftpd.c      8.5 (Berkeley) 4/28/95";
99 #else
100 __RCSID("$NetBSD: ftpd.c,v 1.209 2025/03/26 00:05:56 christos Exp $");
101 #endif
102 #endif /* not lint */
103 
104 /*
105  * FTP server.
106  */
107 #include <sys/param.h>
108 #include <sys/stat.h>
109 #include <sys/ioctl.h>
110 #include <sys/socket.h>
111 #include <sys/wait.h>
112 #include <sys/mman.h>
113 #include <sys/resource.h>
114 
115 #include <netinet/in.h>
116 #include <netinet/in_systm.h>
117 #include <netinet/ip.h>
118 
119 #define   FTP_NAMES
120 #include <arpa/ftp.h>
121 #include <arpa/inet.h>
122 #include <arpa/telnet.h>
123 
124 #include <ctype.h>
125 #include <dirent.h>
126 #include <err.h>
127 #include <errno.h>
128 #include <fcntl.h>
129 #include <fnmatch.h>
130 #include <glob.h>
131 #include <grp.h>
132 #include <limits.h>
133 #include <netdb.h>
134 #include <pwd.h>
135 #include <poll.h>
136 #include <signal.h>
137 #include <stdarg.h>
138 #include <stdio.h>
139 #include <stdlib.h>
140 #include <string.h>
141 #include <syslog.h>
142 #include <time.h>
143 #include <tzfile.h>
144 #include <unistd.h>
145 #include <util.h>
146 #ifdef SUPPORT_UTMP
147 #include <utmp.h>
148 #endif
149 #ifdef SUPPORT_UTMPX
150 #include <utmpx.h>
151 #endif
152 #ifdef SKEY
153 #include <skey.h>
154 #endif
155 #ifdef KERBEROS5
156 #include <com_err.h>
157 #include <krb5/krb5.h>
158 #endif
159 
160 #ifdef    LOGIN_CAP
161 #include <login_cap.h>
162 #endif
163 
164 #ifdef USE_PAM
165 #include <security/pam_appl.h>
166 #endif
167 
168 #include "pfilter.h"
169 
170 #define   GLOBAL
171 #include "extern.h"
172 #include "pathnames.h"
173 #include "version.h"
174 
175 static sig_atomic_t transflag;
176 static sig_atomic_t urgflag;
177 
178 int       data;
179 int       Dflag;
180 int       fflag;
181 int       sflag;
182 int       stru;                         /* avoid C keyword */
183 int       mode;
184 int       dataport;           /* use specific data port */
185 int       dopidfile;                    /* maintain pid file */
186 int       doutmp;                       /* update utmp file */
187 int       dowtmp;                       /* update wtmp file */
188 int       doxferlog;                    /* syslog/write wu-ftpd style xferlog entries */
189 int       xferlogfd;                    /* fd to write wu-ftpd xferlog entries to */
190 int       getnameopts;                  /* flags for use with getname() */
191 int       dropprivs;                    /* if privileges should or have been dropped */
192 int       mapped;                       /* IPv4 connection on AF_INET6 socket */
193 off_t     file_size;
194 off_t     byte_count;
195 static char ttyline[20];
196 
197 #ifdef USE_PAM
198 static int          auth_pam(void);
199 pam_handle_t        *pamh = NULL;
200 #endif
201 
202 #ifdef SUPPORT_UTMP
203 static struct utmp utmp;      /* for utmp */
204 #endif
205 #ifdef SUPPORT_UTMPX
206 static struct utmpx utmpx;    /* for utmpx */
207 #endif
208 
209 static const char *anondir = NULL;
210 static const char *confdir = _DEFAULT_CONFDIR;
211 
212 static char         *curname;           /* current USER name */
213 static size_t       curname_len;                  /* length of curname (include NUL) */
214 
215 #if defined(KERBEROS) || defined(KERBEROS5)
216 int       has_ccache = 0;
217 int       notickets = 1;
218 char      *krbtkfile_env = NULL;
219 char      *tty = ttyline;
220 int       login_krb5_forwardable_tgt = 0;
221 #endif
222 
223 int epsvall = 0;
224 
225 /*
226  * Timeout intervals for retrying connections
227  * to hosts that don't accept PORT cmds.  This
228  * is a kludge, but given the problems with TCP...
229  */
230 #define   SWAITMAX  90        /* wait at most 90 seconds */
231 #define   SWAITINT  5         /* interval between retries */
232 
233 int       swaitmax = SWAITMAX;
234 int       swaitint = SWAITINT;
235 
236 enum send_status {
237           SS_SUCCESS,
238           SS_ABORTED,                             /* transfer aborted */
239           SS_NO_TRANSFER,                         /* no transfer made yet */
240           SS_FILE_ERROR,                          /* file read error */
241           SS_DATA_ERROR                           /* data send error */
242 };
243 
244 static int           bind_pasv_addr(void);
245 static int           checkuser(const char *, const char *, int, int, char **);
246 static int           checkaccess(const char *);
247 static int           checkpassword(const struct passwd *, const char *);
248 static void          do_pass(int, int, const char *);
249 static void          end_login(void);
250 static FILE         *getdatasock(const char *);
251 static char         *gunique(const char *);
252 static void          login_utmp(const char *, const char *, const char *,
253                          struct sockinet *);
254 static void          logremotehost(struct sockinet *);
255 __dead static void   lostconn(int);
256 __dead static void   toolong(int);
257 __dead static void   sigquit(int);
258 static void          sigurg(int);
259 static int           handleoobcmd(void);
260 static int           receive_data(FILE *, FILE *);
261 static int           send_data(FILE *, FILE *, const struct stat *, int);
262 static struct passwd *sgetpwnam(const char *);
263 static int           write_data(int, char *, size_t, off_t *, struct timeval *,
264                          int);
265 static enum send_status
266                      send_data_with_read(int, int, const struct stat *, int);
267 static enum send_status
268                      send_data_with_mmap(int, int, const struct stat *, int);
269 static void          logrusage(const struct rusage *, const struct rusage *);
270 static void          logout_utmp(void);
271 
272 int       main(int, char *[]);
273 
274 #if defined(KERBEROS)
275 int       klogin(struct passwd *, char *, char *, char *);
276 void      kdestroy(void);
277 #endif
278 #if defined(KERBEROS5)
279 int       k5login(struct passwd *, char *, char *, char *);
280 void      k5destroy(void);
281 #endif
282 
283 int
main(int argc,char * argv[])284 main(int argc, char *argv[])
285 {
286           int                 ch, on = 1, tos, keepalive;
287           socklen_t addrlen;
288 #ifdef KERBEROS5
289           krb5_error_code     kerror;
290 #endif
291           char                *p;
292           const char          *xferlogname = NULL;
293           long                l;
294           struct sigaction sa;
295           sa_family_t         af = AF_UNSPEC;
296 
297           connections = 1;
298           ftpd_debug = 0;
299           logging = 0;
300           pdata = -1;
301           Dflag = 0;
302           fflag = 0;
303           sflag = 0;
304           dataport = 0;
305           dopidfile = 1;                /* default: DO use a pid file to count users */
306           doutmp = 0;                   /* default: Do NOT log to utmp */
307           dowtmp = 1;                   /* default: DO log to wtmp */
308           doxferlog = 0;                /* default: Do NOT syslog xferlog */
309           xferlogfd = -1;               /* default: Do NOT write xferlog file */
310           getnameopts = 0;    /* default: xlate addrs to name */
311           dropprivs = 0;
312           mapped = 0;
313           usedefault = 1;
314           emailaddr = NULL;
315           hostname[0] = '\0';
316           homedir[0] = '\0';
317           gidcount = 0;
318           is_oob = 0;
319           version = FTPD_VERSION;
320 
321           /*
322            * LOG_NDELAY sets up the logging connection immediately,
323            * necessary for anonymous ftp's that chroot and can't do it later.
324            */
325           openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
326 
327           while ((ch = getopt(argc, argv,
328               "46a:c:C:Dde:fh:HlL:nP:qQrst:T:uUvV:wWX")) != -1) {
329                     switch (ch) {
330                     case '4':
331                               af = AF_INET;
332                               break;
333 
334                     case '6':
335                               af = AF_INET6;
336                               break;
337 
338                     case 'a':
339                               anondir = optarg;
340                               break;
341 
342                     case 'c':
343                               confdir = optarg;
344                               break;
345 
346                     case 'C':
347                               if ((p = strchr(optarg, '@')) != NULL) {
348                                         *p++ = '\0';
349                                         strlcpy(remotehost, p, MAXHOSTNAMELEN + 1);
350                                         if (inet_pton(AF_INET, p,
351                                             &his_addr.su_addr) == 1) {
352                                                   his_addr.su_family = AF_INET;
353                                                   his_addr.su_len =
354                                                       sizeof(his_addr.si_su.su_sin);
355 #ifdef INET6
356                                         } else if (inet_pton(AF_INET6, p,
357                                             &his_addr.su_6addr) == 1) {
358                                                   his_addr.su_family = AF_INET6;
359                                                   his_addr.su_len =
360                                                       sizeof(his_addr.si_su.su_sin6);
361 #endif
362                                         } else
363                                                   his_addr.su_family = AF_UNSPEC;
364                               }
365                               pw = sgetpwnam(optarg);
366                               exit(checkaccess(optarg) ? 0 : 1);
367                               /* NOTREACHED */
368 
369                     case 'D':
370                               Dflag = 1;
371                               break;
372 
373                     case 'd':
374                     case 'v':           /* deprecated */
375                               ftpd_debug = 1;
376                               break;
377 
378                     case 'e':
379                               emailaddr = optarg;
380                               break;
381 
382                     case 'f':
383                               fflag = 1;
384                               break;
385 
386                     case 'h':
387                               strlcpy(hostname, optarg, sizeof(hostname));
388                               break;
389 
390                     case 'H':
391                               if (gethostname(hostname, sizeof(hostname)) == -1)
392                                         hostname[0] = '\0';
393                               hostname[sizeof(hostname) - 1] = '\0';
394                               break;
395 
396                     case 'l':
397                               logging++;          /* > 1 == extra logging */
398                               break;
399 
400                     case 'L':
401                               xferlogname = optarg;
402                               break;
403 
404                     case 'n':
405                               getnameopts = NI_NUMERICHOST;
406                               break;
407 
408                     case 'P':
409                               errno = 0;
410                               p = NULL;
411                               l = strtol(optarg, &p, 10);
412                               if (errno || *optarg == '\0' || *p != '\0' ||
413                                   l < IPPORT_RESERVED ||
414                                   l > IPPORT_ANONMAX) {
415                                         syslog(LOG_WARNING, "Invalid dataport %s",
416                                             optarg);
417                                         dataport = 0;
418                               }
419                               dataport = (int)l;
420                               break;
421 
422                     case 'q':
423                               dopidfile = 1;
424                               break;
425 
426                     case 'Q':
427                               dopidfile = 0;
428                               break;
429 
430                     case 'r':
431                               dropprivs = 1;
432                               break;
433 
434                     case 's':
435                               sflag = 1;
436                               break;
437 
438                     case 't':
439                     case 'T':
440                               syslog(LOG_WARNING,
441                                   "-%c has been deprecated in favour of ftpd.conf",
442                                   ch);
443                               break;
444 
445                     case 'u':
446                               doutmp = 1;
447                               break;
448 
449                     case 'U':
450                               doutmp = 0;
451                               break;
452 
453                     case 'V':
454                               if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
455                                         version = NULL;
456                               else
457                                         version = ftpd_strdup(optarg);
458                               break;
459 
460                     case 'w':
461                               dowtmp = 1;
462                               break;
463 
464                     case 'W':
465                               dowtmp = 0;
466                               break;
467 
468                     case 'X':
469                               doxferlog |= 1;
470                               break;
471 
472                     default:
473                               if (optopt == 'a' || optopt == 'C')
474                                         exit(1);
475                               syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
476                               break;
477                     }
478           }
479           if (EMPTYSTR(confdir))
480                     confdir = _DEFAULT_CONFDIR;
481 
482           pfilter_open();
483 
484           if (dowtmp) {
485 #ifdef SUPPORT_UTMPX
486                     ftpd_initwtmpx();
487 #endif
488 #ifdef SUPPORT_UTMP
489                     ftpd_initwtmp();
490 #endif
491           }
492           errno = 0;
493           l = sysconf(_SC_LOGIN_NAME_MAX);
494           if (l == -1 && errno != 0) {
495                     syslog(LOG_ERR, "sysconf _SC_LOGIN_NAME_MAX: %m");
496                     exit(1);
497           } else if (l <= 0) {
498                     syslog(LOG_WARNING, "using conservative LOGIN_NAME_MAX value");
499                     curname_len = _POSIX_LOGIN_NAME_MAX;
500           } else
501                     curname_len = (size_t)l;
502           curname = malloc(curname_len);
503           if (curname == NULL) {
504                     syslog(LOG_ERR, "malloc: %m");
505                     exit(1);
506           }
507           curname[0] = '\0';
508 
509           if (Dflag) {
510                     int error, fd, i, n, *socks;
511                     struct pollfd *fds;
512                     struct addrinfo hints, *res, *res0;
513 
514                     if (!fflag && daemon(1, 0) == -1) {
515                               syslog(LOG_ERR, "failed to daemonize: %m");
516                               exit(1);
517                     }
518                     (void)memset(&sa, 0, sizeof(sa));
519                     sa.sa_handler = SIG_IGN;
520                     sa.sa_flags = SA_NOCLDWAIT;
521                     sigemptyset(&sa.sa_mask);
522                     (void)sigaction(SIGCHLD, &sa, NULL);
523 
524                     (void)memset(&hints, 0, sizeof(hints));
525                     hints.ai_flags = AI_PASSIVE;
526                     hints.ai_family = af;
527                     hints.ai_socktype = SOCK_STREAM;
528                     error = getaddrinfo(NULL, "ftp", &hints, &res0);
529                     if (error) {
530                               syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
531                               exit(1);
532                     }
533 
534                     for (n = 0, res = res0; res != NULL; res = res->ai_next)
535                               n++;
536                     if (n == 0) {
537                               syslog(LOG_ERR, "no addresses available");
538                               exit(1);
539                     }
540                     socks = malloc(n * sizeof(int));
541                     fds = malloc(n * sizeof(struct pollfd));
542                     if (socks == NULL || fds == NULL) {
543                               syslog(LOG_ERR, "malloc: %m");
544                               exit(1);
545                     }
546 
547                     for (n = 0, res = res0; res != NULL; res = res->ai_next) {
548                               socks[n] = socket(res->ai_family, res->ai_socktype,
549                                   res->ai_protocol);
550                               if (socks[n] == -1)
551                                         continue;
552                               (void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR,
553                                   &on, sizeof(on));
554                               if (bind(socks[n], res->ai_addr, res->ai_addrlen)
555                                   == -1) {
556                                         (void)close(socks[n]);
557                                         continue;
558                               }
559                               if (listen(socks[n], 12) == -1) {
560                                         (void)close(socks[n]);
561                                         continue;
562                               }
563 
564                               fds[n].fd = socks[n];
565                               fds[n].events = POLLIN;
566                               n++;
567                     }
568                     if (n == 0) {
569                               syslog(LOG_ERR, "%m");
570                               exit(1);
571                     }
572                     freeaddrinfo(res0);
573 
574                     if (pidfile(NULL) == -1)
575                               syslog(LOG_ERR, "failed to write a pid file: %m");
576 
577                     for (;;) {
578                               if (poll(fds, n, INFTIM) == -1) {
579                                         if (errno == EINTR)
580                                                   continue;
581                                         syslog(LOG_ERR, "poll: %m");
582                                         exit(1);
583                               }
584                               for (i = 0; i < n; i++) {
585                                         if (fds[i].revents & POLLIN) {
586                                                   fd = accept(fds[i].fd, NULL, NULL);
587                                                   if (fd == -1) {
588                                                             syslog(LOG_ERR, "accept: %m");
589                                                             continue;
590                                                   }
591                                                   switch (fork()) {
592                                                   case -1:
593                                                             syslog(LOG_ERR, "fork: %m");
594                                                             break;
595                                                   case 0:
596                                                             goto child;
597                                                             /* NOTREACHED */
598                                                   }
599                                                   (void)close(fd);
600                                         }
601                               }
602                     }
603  child:
604                     (void)dup2(fd, STDIN_FILENO);
605                     (void)dup2(fd, STDOUT_FILENO);
606                     (void)dup2(fd, STDERR_FILENO);
607                     for (i = 0; i < n; i++)
608                               (void)close(socks[i]);
609           }
610 
611           memset((char *)&his_addr, 0, sizeof(his_addr));
612           addrlen = sizeof(his_addr.si_su);
613           if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
614                     syslog((errno == ENOTCONN) ? LOG_NOTICE : LOG_ERR,
615                         "getpeername (%s): %m",argv[0]);
616                     exit(1);
617           }
618           his_addr.su_len = addrlen;
619           memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr));
620           addrlen = sizeof(ctrl_addr.si_su);
621           if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
622                     syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
623                     exit(1);
624           }
625           ctrl_addr.su_len = addrlen;
626 #ifdef INET6
627           if (his_addr.su_family == AF_INET6
628            && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) {
629 #if 1
630                     /*
631                      * IPv4 control connection arrived to AF_INET6 socket.
632                      * I hate to do this, but this is the easiest solution.
633                      *
634                      * The assumption is untrue on SIIT environment.
635                      */
636                     struct sockinet tmp_addr;
637                     const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
638 
639                     tmp_addr = his_addr;
640                     memset(&his_addr, 0, sizeof(his_addr));
641                     his_addr.su_family = AF_INET;
642                     his_addr.su_len = sizeof(his_addr.si_su.su_sin);
643                     memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
644                         sizeof(his_addr.su_addr));
645                     his_addr.su_port = tmp_addr.su_port;
646 
647                     tmp_addr = ctrl_addr;
648                     memset(&ctrl_addr, 0, sizeof(ctrl_addr));
649                     ctrl_addr.su_family = AF_INET;
650                     ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin);
651                     memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
652                         sizeof(ctrl_addr.su_addr));
653                     ctrl_addr.su_port = tmp_addr.su_port;
654 #else
655                     while (fgets(line, sizeof(line), fd) != NULL) {
656                               if ((cp = strchr(line, '\n')) != NULL)
657                                         *cp = '\0';
658                               reply(-530, "%s", line);
659                     }
660                     (void) fflush(stdout);
661                     (void) fclose(fd);
662                     reply(530,
663                         "Connection from IPv4 mapped address is not supported.");
664                     exit(0);
665 #endif
666 
667                     mapped = 1;
668           } else
669 #endif /* INET6 */
670                     mapped = 0;
671 #ifdef IP_TOS
672           if (!mapped && his_addr.su_family == AF_INET) {
673                     tos = IPTOS_LOWDELAY;
674                     if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
675                                      sizeof(int)) < 0)
676                               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
677           }
678 #endif
679           /* if the hostname hasn't been given, attempt to determine it */
680           if (hostname[0] == '\0') {
681                     if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su,
682                         ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0,
683                               getnameopts) != 0)
684                               (void)gethostname(hostname, sizeof(hostname));
685                     hostname[sizeof(hostname) - 1] = '\0';
686           }
687 
688           /* set this here so klogin can use it... */
689           (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
690 
691           (void) freopen(_PATH_DEVNULL, "w", stderr);
692 
693           memset(&sa, 0, sizeof(sa));
694           sa.sa_handler = SIG_DFL;
695           sa.sa_flags = SA_RESTART;
696           sigemptyset(&sa.sa_mask);
697           (void) sigaction(SIGCHLD, &sa, NULL);
698 
699           sa.sa_handler = sigquit;
700           sa.sa_flags = SA_RESTART;
701           sigfillset(&sa.sa_mask);      /* block all sigs in these handlers */
702           (void) sigaction(SIGHUP, &sa, NULL);
703           (void) sigaction(SIGINT, &sa, NULL);
704           (void) sigaction(SIGQUIT, &sa, NULL);
705           (void) sigaction(SIGTERM, &sa, NULL);
706           sa.sa_handler = lostconn;
707           (void) sigaction(SIGPIPE, &sa, NULL);
708           sa.sa_handler = toolong;
709           (void) sigaction(SIGALRM, &sa, NULL);
710           sa.sa_handler = sigurg;
711           (void) sigaction(SIGURG, &sa, NULL);
712 
713           /* Try to handle urgent data inline */
714 #ifdef SO_OOBINLINE
715           if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
716                     syslog(LOG_WARNING, "setsockopt: %m");
717 #endif
718           /* Set keepalives on the socket to detect dropped connections.  */
719 #ifdef SO_KEEPALIVE
720           keepalive = 1;
721           if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive,
722               sizeof(int)) < 0)
723                     syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
724 #endif
725 
726 #ifdef    F_SETOWN
727           if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
728                     syslog(LOG_WARNING, "fcntl F_SETOWN: %m");
729 #endif
730           logremotehost(&his_addr);
731           /*
732            * Set up default state
733            */
734           data = -1;
735           type = TYPE_A;
736           form = FORM_N;
737           stru = STRU_F;
738           mode = MODE_S;
739           tmpline[0] = '\0';
740           hasyyerrored = 0;
741 
742 #ifdef KERBEROS5
743           kerror = krb5_init_context(&kcontext);
744           if (kerror) {
745                     syslog(LOG_ERR, "%s when initializing Kerberos context",
746                         error_message(kerror));
747                     exit(0);
748           }
749 #endif /* KERBEROS5 */
750 
751           init_curclass();
752           curclass.timeout = 300;                 /* 5 minutes, as per login(1) */
753           curclass.type = CLASS_REAL;
754 
755           /* If logins are disabled, print out the message. */
756           if (display_file(_PATH_NOLOGIN, 530)) {
757                     reply(530, "System not available.");
758                     exit(0);
759           }
760           (void)display_file(conffilename(_NAME_FTPWELCOME), 220);
761                     /* reply(220,) must follow */
762           if (EMPTYSTR(version))
763                     reply(220, "%s FTP server ready.", hostname);
764           else
765                     reply(220, "%s FTP server (%s) ready.", hostname, version);
766 
767           if (xferlogname != NULL) {
768                     xferlogfd = open(xferlogname, O_WRONLY | O_APPEND | O_CREAT,
769                         0660);
770                     if (xferlogfd == -1)
771                               syslog(LOG_WARNING, "open xferlog `%s': %m",
772                                   xferlogname);
773                     else
774                               doxferlog |= 2;
775           }
776 
777           ftp_loop();
778           /* NOTREACHED */
779 }
780 
781 static void
lostconn(int signo __unused)782 lostconn(int signo __unused)
783 {
784 
785           if (ftpd_debug)
786                     syslog(LOG_DEBUG, "lost connection");
787           dologout(1);
788 }
789 
790 static void
toolong(int signo __unused)791 toolong(int signo __unused)
792 {
793 
794                     /* XXXSIGRACE */
795           reply(421,
796               "Timeout (" LLF " seconds): closing control connection.",
797               (LLT)curclass.timeout);
798           if (logging)
799                     syslog(LOG_INFO, "User %s timed out after " LLF " seconds",
800                         (pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout);
801           dologout(1);
802 }
803 
804 static void
sigquit(int signo)805 sigquit(int signo)
806 {
807 
808           if (ftpd_debug)
809                     syslog(LOG_DEBUG, "got signal %d", signo);
810           dologout(1);
811 }
812 
813 static void
sigurg(int signo __unused)814 sigurg(int signo __unused)
815 {
816 
817           urgflag = 1;
818 }
819 
820 
821 /*
822  * Save the result of a getpwnam.  Used for USER command, since
823  * the data returned must not be clobbered by any other command
824  * (e.g., globbing).
825  */
826 static struct passwd *
sgetpwnam(const char * name)827 sgetpwnam(const char *name)
828 {
829           static struct passwd save;
830           struct passwd *p;
831 
832           if ((p = getpwnam(name)) == NULL)
833                     return (p);
834           if (save.pw_name) {
835                     free((char *)save.pw_name);
836                     memset(save.pw_passwd, 0, strlen(save.pw_passwd));
837                     free((char *)save.pw_passwd);
838                     free((char *)save.pw_gecos);
839                     free((char *)save.pw_dir);
840                     free((char *)save.pw_shell);
841           }
842           save = *p;
843           save.pw_name = ftpd_strdup(p->pw_name);
844           save.pw_passwd = ftpd_strdup(p->pw_passwd);
845           save.pw_gecos = ftpd_strdup(p->pw_gecos);
846           save.pw_dir = ftpd_strdup(p->pw_dir);
847           save.pw_shell = ftpd_strdup(p->pw_shell);
848           return (&save);
849 }
850 
851 static int          login_attempts;     /* number of failed login attempts */
852 static int          askpasswd;          /* had USER command, ask for PASSwd */
853 static int          permitted;          /* USER permitted */
854 
855 /*
856  * USER command.
857  * Sets global passwd pointer pw if named account exists and is acceptable;
858  * sets askpasswd if a PASS command is expected.  If logged in previously,
859  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
860  * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
861  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
862  * requesting login privileges.  Disallow anyone who does not have a standard
863  * shell as returned by getusershell().  Disallow anyone mentioned in the file
864  * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
865  */
866 void
user(const char * name)867 user(const char *name)
868 {
869           char      *class;
870 #ifdef    LOGIN_CAP
871           login_cap_t *lc = NULL;
872 #endif
873 #ifdef USE_PAM
874           int e;
875 #endif
876 
877           class = NULL;
878           if (logged_in) {
879                     switch (curclass.type) {
880                     case CLASS_GUEST:
881                               reply(530, "Can't change user from guest login.");
882                               return;
883                     case CLASS_CHROOT:
884                               reply(530, "Can't change user from chroot user.");
885                               return;
886                     case CLASS_REAL:
887                               if (dropprivs) {
888                                         reply(530, "Can't change user.");
889                                         return;
890                               }
891                               end_login();
892                               break;
893                     default:
894                               abort();
895                     }
896           }
897 
898 #if defined(KERBEROS)
899           kdestroy();
900 #endif
901 #if defined(KERBEROS5)
902           k5destroy();
903 #endif
904 
905           curclass.type = CLASS_REAL;
906           askpasswd = 0;
907           permitted = 0;
908 
909           if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
910                               /* need `pw' setup for checkaccess() and checkuser () */
911                     if ((pw = sgetpwnam("ftp")) == NULL)
912                               reply(530, "User %s unknown.", name);
913                     else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
914                               reply(530, "User %s access denied.", name);
915                     else {
916                               curclass.type = CLASS_GUEST;
917                               askpasswd = 1;
918                               reply(331,
919                                   "Guest login ok, type your name as password.");
920                     }
921                     if (!askpasswd) {
922                               if (logging)
923                                         syslog(LOG_NOTICE,
924                                             "ANONYMOUS FTP LOGIN REFUSED FROM %s",
925                                             remoteloghost);
926                               end_login();
927                               goto cleanup_user;
928                     }
929                     name = "ftp";
930           } else
931                     pw = sgetpwnam(name);
932 
933           strlcpy(curname, name, curname_len);
934 
935                               /* check user in /etc/ftpusers, and setup class */
936           permitted = checkuser(_NAME_FTPUSERS, curname, 1, 0, &class);
937 
938                               /* check user in /etc/ftpchroot */
939 #ifdef    LOGIN_CAP
940           lc = login_getpwclass(pw);
941 #endif
942           if (checkuser(_NAME_FTPCHROOT, curname, 0, 0, NULL)
943 #ifdef    LOGIN_CAP /* Allow login.conf configuration as well */
944               || login_getcapbool(lc, "ftp-chroot", 0)
945 #endif
946           ) {
947                     if (curclass.type == CLASS_GUEST) {
948                               syslog(LOG_NOTICE,
949               "Can't change guest user to chroot class; remove entry in %s",
950                                   _NAME_FTPCHROOT);
951                               exit(1);
952                     }
953                     curclass.type = CLASS_CHROOT;
954           }
955 
956                               /* determine default class */
957           if (class == NULL) {
958                     switch (curclass.type) {
959                     case CLASS_GUEST:
960                               class = ftpd_strdup("guest");
961                               break;
962                     case CLASS_CHROOT:
963                               class = ftpd_strdup("chroot");
964                               break;
965                     case CLASS_REAL:
966                               class = ftpd_strdup("real");
967                               break;
968                     default:
969                               syslog(LOG_ERR, "unknown curclass.type %d; aborting",
970                                   curclass.type);
971                               abort();
972                     }
973           }
974                               /* parse ftpd.conf, setting up various parameters */
975           parse_conf(class);
976                               /* if not guest user, check for valid shell */
977           if (pw == NULL)
978                     permitted = 0;
979           else {
980                     const char          *cp, *shell;
981 
982                     if ((shell = pw->pw_shell) == NULL || *shell == 0)
983                               shell = _PATH_BSHELL;
984                     while ((cp = getusershell()) != NULL)
985                               if (strcmp(cp, shell) == 0)
986                                         break;
987                     endusershell();
988                     if (cp == NULL && curclass.type != CLASS_GUEST)
989                               permitted = 0;
990           }
991 
992                               /* deny quickly (after USER not PASS) if requested */
993           if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) {
994                     reply(530, "User %s may not use FTP.", curname);
995                     if (logging)
996                               syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
997                                   remoteloghost, curname);
998                     end_login();
999                     goto cleanup_user;
1000           }
1001 
1002                               /* if haven't asked yet (i.e, not anon), ask now */
1003           if (!askpasswd) {
1004                     askpasswd = 1;
1005 #ifdef USE_PAM
1006                     e = auth_pam();               /* this does reply(331, ...) */
1007                     do_pass(1, e, "");
1008                     goto cleanup_user;
1009 #else /* !USE_PAM */
1010 #ifdef SKEY
1011                     if (skey_haskey(curname) == 0) {
1012                               const char *myskey;
1013 
1014                               myskey = skey_keyinfo(curname);
1015                               reply(331, "Password [ %s ] required for %s.",
1016                                   myskey ? myskey : "error getting challenge",
1017                                   curname);
1018                     } else
1019 #endif
1020                               reply(331, "Password required for %s.", curname);
1021 #endif /* !USE_PAM */
1022           }
1023 
1024  cleanup_user:
1025 #ifdef LOGIN_CAP
1026           login_close(lc);
1027 #endif
1028           /*
1029            * Delay before reading passwd after first failed
1030            * attempt to slow down passwd-guessing programs.
1031            */
1032           if (login_attempts)
1033                     sleep((unsigned) login_attempts);
1034 
1035           if (class)
1036                     free(class);
1037 }
1038 
1039 /*
1040  * Determine whether something is to happen (allow access, chroot)
1041  * for a user. Each line is a shell-style glob followed by
1042  * `yes' or `no'.
1043  *
1044  * For backward compatibility, `allow' and `deny' are synonymns
1045  * for `yes' and `no', respectively.
1046  *
1047  * Each glob is matched against the username in turn, and the first
1048  * match found is used. If no match is found, the result is the
1049  * argument `def'. If a match is found but without and explicit
1050  * `yes'/`no', the result is the opposite of def.
1051  *
1052  * If the file doesn't exist at all, the result is the argument
1053  * `nofile'
1054  *
1055  * Any line starting with `#' is considered a comment and ignored.
1056  *
1057  * Returns 0 if the user is denied, or 1 if they are allowed.
1058  *
1059  * NOTE: needs struct passwd *pw setup before use.
1060  */
1061 static int
checkuser(const char * fname,const char * name,int def,int nofile,char ** retclass)1062 checkuser(const char *fname, const char *name, int def, int nofile,
1063               char **retclass)
1064 {
1065           FILE      *fd;
1066           int        retval;
1067           char      *word, *perm, *class, *buf, *p;
1068           size_t     len, line;
1069 
1070           retval = def;
1071           if (retclass != NULL)
1072                     *retclass = NULL;
1073           if ((fd = fopen(conffilename(fname), "r")) == NULL)
1074                     return nofile;
1075 
1076           line = 0;
1077           for (;
1078               (buf = fparseln(fd, &len, &line, NULL, FPARSELN_UNESCCOMM |
1079                                   FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
1080               free(buf), buf = NULL) {
1081                     word = perm = class = NULL;
1082                     p = buf;
1083                     if (len < 1)
1084                               continue;
1085                     if (p[len - 1] == '\n')
1086                               p[--len] = '\0';
1087                     if (EMPTYSTR(p))
1088                               continue;
1089 
1090                     NEXTWORD(p, word);
1091                     NEXTWORD(p, perm);
1092                     NEXTWORD(p, class);
1093                     if (EMPTYSTR(word))
1094                               continue;
1095                     if (!EMPTYSTR(class)) {
1096                               if (strcasecmp(class, "all") == 0 ||
1097                                   strcasecmp(class, "none") == 0) {
1098                                         syslog(LOG_WARNING,
1099                     "%s line %d: illegal user-defined class `%s' - skipping entry",
1100                                                       fname, (int)line, class);
1101                                         continue;
1102                               }
1103                     }
1104 
1105                                                   /* have a host specifier */
1106                     if ((p = strchr(word, '@')) != NULL) {
1107                               unsigned char       net[16], mask[16], *addr;
1108                               int                 addrlen, bits, bytes, a;
1109 
1110                               *p++ = '\0';
1111                                                   /* check against network or CIDR */
1112                               memset(net, 0x00, sizeof(net));
1113                               if ((bits = inet_net_pton(his_addr.su_family, p, net,
1114                                   sizeof(net))) != -1) {
1115 #ifdef INET6
1116                                         if (his_addr.su_family == AF_INET) {
1117 #endif
1118                                                   addrlen = 4;
1119                                                   addr = (unsigned char *)&his_addr.su_addr;
1120 #ifdef INET6
1121                                         } else {
1122                                                   addrlen = 16;
1123                                                   addr = (unsigned char *)&his_addr.su_6addr;
1124                                         }
1125 #endif
1126                                         bytes = bits / 8;
1127                                         bits = bits % 8;
1128                                         if (bytes > 0)
1129                                                   memset(mask, 0xFF, bytes);
1130                                         if (bytes < addrlen)
1131                                                   mask[bytes] = 0xFF << (8 - bits);
1132                                         if (bytes + 1 < addrlen)
1133                                                   memset(mask + bytes + 1, 0x00,
1134                                                       addrlen - bytes - 1);
1135                                         for (a = 0; a < addrlen; a++)
1136                                                   if ((addr[a] & mask[a]) != net[a])
1137                                                             break;
1138                                         if (a < addrlen)
1139                                                   continue;
1140 
1141                                                   /* check against hostname glob */
1142                               } else if (fnmatch(p, remotehost, FNM_CASEFOLD) != 0)
1143                                         continue;
1144                     }
1145 
1146                                                   /* have a group specifier */
1147                     if ((p = strchr(word, ':')) != NULL) {
1148                               gid_t     *groups, *ng;
1149                               int        gsize, i, found;
1150 
1151                               if (pw == NULL)
1152                                         continue; /* no match for unknown user */
1153                               *p++ = '\0';
1154                               groups = NULL;
1155                               gsize = 16;
1156                               do {
1157                                         ng = realloc(groups, gsize * sizeof(gid_t));
1158                                         if (ng == NULL)
1159                                                   fatal(
1160                                                       "Local resource failure: realloc");
1161                                         groups = ng;
1162                               } while (getgrouplist(pw->pw_name, pw->pw_gid,
1163                                                             groups, &gsize) == -1);
1164                               found = 0;
1165                               for (i = 0; i < gsize; i++) {
1166                                         struct group *g;
1167 
1168                                         if ((g = getgrgid(groups[i])) == NULL)
1169                                                   continue;
1170                                         if (fnmatch(p, g->gr_name, 0) == 0) {
1171                                                   found = 1;
1172                                                   break;
1173                                         }
1174                               }
1175                               free(groups);
1176                               if (!found)
1177                                         continue;
1178                     }
1179 
1180                                                   /* check against username glob */
1181                     if (fnmatch(word, name, 0) != 0)
1182                               continue;
1183 
1184                     if (perm != NULL &&
1185                         ((strcasecmp(perm, "allow") == 0) ||
1186                          (strcasecmp(perm, "yes") == 0)))
1187                               retval = 1;
1188                     else if (perm != NULL &&
1189                         ((strcasecmp(perm, "deny") == 0) ||
1190                          (strcasecmp(perm, "no") == 0)))
1191                               retval = 0;
1192                     else
1193                               retval = !def;
1194                     if (!EMPTYSTR(class) && retclass != NULL)
1195                               *retclass = ftpd_strdup(class);
1196                     free(buf);
1197                     break;
1198           }
1199           (void) fclose(fd);
1200           return (retval);
1201 }
1202 
1203 /*
1204  * Check if user is allowed by /etc/ftpusers
1205  * returns 1 for yes, 0 for no
1206  *
1207  * NOTE: needs struct passwd *pw setup (for checkuser())
1208  */
1209 static int
checkaccess(const char * name)1210 checkaccess(const char *name)
1211 {
1212 
1213           return (checkuser(_NAME_FTPUSERS, name, 1, 0, NULL));
1214 }
1215 
1216 static void
login_utmp(const char * line,const char * name,const char * host,struct sockinet * haddr)1217 login_utmp(const char *line, const char *name, const char *host,
1218     struct sockinet *haddr)
1219 {
1220 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1221           struct timeval tv;
1222           (void)gettimeofday(&tv, NULL);
1223 #endif
1224 #ifdef SUPPORT_UTMPX
1225           if (doutmp) {
1226                     (void)memset(&utmpx, 0, sizeof(utmpx));
1227                     utmpx.ut_tv = tv;
1228                     utmpx.ut_pid = getpid();
1229                     utmpx.ut_id[0] = 'f';
1230                     utmpx.ut_id[1] = 't';
1231                     utmpx.ut_id[2] = 'p';
1232                     utmpx.ut_id[3] = '*';
1233                     utmpx.ut_type = USER_PROCESS;
1234                     (void)strncpy(utmpx.ut_name, name, sizeof(utmpx.ut_name));
1235                     (void)strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
1236                     (void)strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
1237                     (void)memcpy(&utmpx.ut_ss, &haddr->si_su, haddr->su_len);
1238                     ftpd_loginx(&utmpx);
1239           }
1240           if (dowtmp)
1241                     ftpd_logwtmpx(line, name, host, haddr, 0, USER_PROCESS);
1242 #endif
1243 #ifdef SUPPORT_UTMP
1244           if (doutmp) {
1245                     (void)memset(&utmp, 0, sizeof(utmp));
1246                     (void)time(&utmp.ut_time);
1247                     (void)strncpy(utmp.ut_name, name, sizeof(utmp.ut_name));
1248                     (void)strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
1249                     (void)strncpy(utmp.ut_host, host, sizeof(utmp.ut_host));
1250                     ftpd_login(&utmp);
1251           }
1252           if (dowtmp)
1253                     ftpd_logwtmp(line, name, host);
1254 #endif
1255 }
1256 
1257 static void
logout_utmp(void)1258 logout_utmp(void)
1259 {
1260 #ifdef SUPPORT_UTMPX
1261           int okwtmpx = dowtmp;
1262 #endif
1263 #ifdef SUPPORT_UTMP
1264           int okwtmp = dowtmp;
1265 #endif
1266           if (logged_in) {
1267 #ifdef SUPPORT_UTMPX
1268                     if (doutmp)
1269                               okwtmpx &= ftpd_logoutx(ttyline, 0, DEAD_PROCESS);
1270                     if (okwtmpx)
1271                               ftpd_logwtmpx(ttyline, "", "", NULL, 0, DEAD_PROCESS);
1272 #endif
1273 #ifdef SUPPORT_UTMP
1274                     if (doutmp)
1275                               okwtmp &= ftpd_logout(ttyline);
1276                     if (okwtmp)
1277                               ftpd_logwtmp(ttyline, "", "");
1278 #endif
1279           }
1280 }
1281 
1282 /*
1283  * Terminate login as previous user (if any), resetting state;
1284  * used when USER command is given or login fails.
1285  */
1286 static void
end_login(void)1287 end_login(void)
1288 {
1289 #ifdef USE_PAM
1290           int e;
1291 #endif
1292           logout_utmp();
1293           show_chdir_messages(-1);                /* flush chdir cache */
1294           if (pw != NULL && pw->pw_passwd != NULL)
1295                     memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
1296           pw = NULL;
1297           logged_in = 0;
1298           askpasswd = 0;
1299           permitted = 0;
1300           quietmessages = 0;
1301           gidcount = 0;
1302           curclass.type = CLASS_REAL;
1303           if (!dropprivs) {
1304                     if (seteuid((uid_t)0) < 0) {
1305                               syslog(LOG_NOTICE, "end_login: can't seteuid 0: %m");
1306                               fatal("Can't reset privileges.");
1307                     }
1308           }
1309 #ifdef    LOGIN_CAP
1310           setusercontext(NULL, getpwuid(0), 0,
1311                            LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1312 #endif
1313 #ifdef USE_PAM
1314           if (pamh) {
1315                     if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1316                               syslog(LOG_ERR, "pam_setcred: %s",
1317                                   pam_strerror(pamh, e));
1318                     if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1319                               syslog(LOG_ERR, "pam_close_session: %s",
1320                                   pam_strerror(pamh, e));
1321                     if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1322                               syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1323                     pamh = NULL;
1324           }
1325 #endif
1326 }
1327 
1328 void
pass(const char * passwd)1329 pass(const char *passwd)
1330 {
1331           do_pass(0, 0, passwd);
1332 }
1333 
1334 /*
1335  * Perform the passwd confirmation and login.
1336  *
1337  * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval.
1338  * This is the traditional PASS implementation.
1339  *
1340  * If pass_checked is non-zero, use pass_rval and ignore passwd.
1341  * This is used by auth_pam() which has already parsed PASS.
1342  * This only applies to curclass.type != CLASS_GUEST.
1343  */
1344 static void
do_pass(int pass_checked,int pass_rval,const char * passwd)1345 do_pass(int pass_checked, int pass_rval, const char *passwd)
1346 {
1347           int                  rval;
1348           char                 root[MAXPATHLEN];
1349 #ifdef    LOGIN_CAP
1350           login_cap_t *lc = NULL;
1351 #endif
1352 #ifdef USE_PAM
1353           int e;
1354 #endif
1355 
1356           rval = 1;
1357 
1358           if (logged_in || askpasswd == 0) {
1359                     reply(503, "Login with USER first.");
1360                     return;
1361           }
1362           askpasswd = 0;
1363           if (curclass.type != CLASS_GUEST) {
1364                               /* "ftp" is the only account allowed with no password */
1365                     if (pw == NULL) {
1366                               rval = 1; /* failure below */
1367                               goto skip;
1368                     }
1369                     if (pass_checked) { /* password validated in user() */
1370                               rval = pass_rval;
1371                               goto skip;
1372                     }
1373 #ifdef USE_PAM
1374                     syslog(LOG_ERR, "do_pass: USE_PAM shouldn't get here");
1375                     rval = 1;
1376                     goto skip;
1377 #endif
1378 #if defined(KERBEROS)
1379                     if (klogin(pw, "", hostname, (char *)passwd) == 0) {
1380                               rval = 0;
1381                               goto skip;
1382                     }
1383 #endif
1384 #if defined(KERBEROS5)
1385                     if (k5login(pw, "", hostname, (char *)passwd) == 0) {
1386                               rval = 0;
1387                               goto skip;
1388                     }
1389 #endif
1390 #ifdef SKEY
1391                     if (skey_haskey(pw->pw_name) == 0) {
1392                               char *p;
1393                               int r;
1394 
1395                               p = ftpd_strdup(passwd);
1396                               r = skey_passcheck(pw->pw_name, p);
1397                               free(p);
1398                               if (r != -1) {
1399                                         rval = 0;
1400                                         goto skip;
1401                               }
1402                     }
1403 #endif
1404                     if (!sflag)
1405                               rval = checkpassword(pw, passwd);
1406                     else
1407                               rval = 1;
1408 
1409  skip:
1410 
1411                               /*
1412                                * If rval > 0, the user failed the authentication check
1413                                * above.  If rval == 0, either Kerberos or local
1414                                * authentication succeeded.
1415                                */
1416                     if (rval) {
1417                               reply(530, "%s", rval == 2 ? "Password expired." :
1418                                   "Login incorrect.");
1419                               pfilter_notify(1, rval == 2 ? "exppass" : "badpass");
1420                               if (logging) {
1421                                         syslog(LOG_NOTICE,
1422                                             "FTP LOGIN FAILED FROM %s", remoteloghost);
1423                                         syslog(LOG_AUTHPRIV | LOG_NOTICE,
1424                                             "FTP LOGIN FAILED FROM %s, %s",
1425                                             remoteloghost, curname);
1426                               }
1427                               pw = NULL;
1428                               if (login_attempts++ >= 5) {
1429                                         syslog(LOG_NOTICE,
1430                                             "repeated login failures from %s",
1431                                             remoteloghost);
1432                                         exit(0);
1433                               }
1434                               return;
1435                     } else
1436                               pfilter_notify(0, "success");
1437           }
1438 
1439                               /* password ok; check if anything else prevents login */
1440           if (! permitted) {
1441                     reply(530, "User %s may not use FTP.", pw->pw_name);
1442                     if (logging)
1443                               syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
1444                                   remoteloghost, pw->pw_name);
1445                     goto bad;
1446           }
1447 
1448           login_attempts = 0;           /* this time successful */
1449           if (setegid((gid_t)pw->pw_gid) < 0) {
1450                     syslog(LOG_NOTICE, "user %s: can't setegid: %m", pw->pw_name);
1451                     fatal("Can't drop privileges.");
1452           }
1453 #ifdef    LOGIN_CAP
1454           if ((lc = login_getpwclass(pw)) != NULL) {
1455 #ifdef notyet
1456                     char      remote_ip[NI_MAXHOST];
1457 
1458                     if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1459                               remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1460                               NI_NUMERICHOST))
1461                                         *remote_ip = 0;
1462                     remote_ip[sizeof(remote_ip) - 1] = 0;
1463                     if (!auth_hostok(lc, remotehost, remote_ip)) {
1464                               pfilter_notify(1, "bannedhost");
1465                               syslog(LOG_INFO|LOG_AUTH,
1466                                   "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1467                                   pw->pw_name);
1468                               reply(530, "Permission denied.");
1469                               pw = NULL;
1470                               return;
1471                     }
1472                     if (!auth_timeok(lc, time(NULL))) {
1473                               reply(530, "Login not available right now.");
1474                               pw = NULL;
1475                               return;
1476                     }
1477 #endif
1478           }
1479           setsid();
1480           setusercontext(lc, pw, 0,
1481                     LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY|
1482                     LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1483 #else
1484           (void) initgroups(pw->pw_name, pw->pw_gid);
1485                               /* cache groups for cmds.c::matchgroup() */
1486 #endif
1487 #ifdef USE_PAM
1488           if (pamh) {
1489                     if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
1490                               syslog(LOG_ERR, "pam_open_session: %s",
1491                                   pam_strerror(pamh, e));
1492                     } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
1493                         != PAM_SUCCESS) {
1494                               syslog(LOG_ERR, "pam_setcred: %s",
1495                                   pam_strerror(pamh, e));
1496                     }
1497           }
1498 #endif
1499           gidcount = getgroups(0, NULL);
1500           if (gidlist)
1501                     free(gidlist);
1502           gidlist = malloc(gidcount * sizeof *gidlist);
1503           gidcount = getgroups(gidcount, gidlist);
1504 
1505           /* open utmp/wtmp before chroot */
1506           login_utmp(ttyline, pw->pw_name, remotehost, &his_addr);
1507 
1508           logged_in = 1;
1509 
1510           connections = 1;
1511           if (dopidfile)
1512                     count_users();
1513           if (curclass.limit != -1 && connections > curclass.limit) {
1514                     if (! EMPTYSTR(curclass.limitfile))
1515                               (void)display_file(conffilename(curclass.limitfile),
1516                                   530);
1517                     reply(530,
1518                         "User %s access denied, connection limit of " LLF
1519                         " reached.",
1520                         pw->pw_name, (LLT)curclass.limit);
1521                     syslog(LOG_NOTICE,
1522                         "Maximum connection limit of " LLF
1523                         " for class %s reached, login refused for %s",
1524                         (LLT)curclass.limit, curclass.classname, pw->pw_name);
1525                     goto bad;
1526           }
1527 
1528           homedir[0] = '/';
1529           switch (curclass.type) {
1530           case CLASS_GUEST:
1531                               /*
1532                                * We MUST do a chdir() after the chroot. Otherwise
1533                                * the old current directory will be accessible as "."
1534                                * outside the new root!
1535                                */
1536                     format_path(root,
1537                         curclass.chroot ? curclass.chroot :
1538                         anondir ? anondir :
1539                         pw->pw_dir);
1540                     format_path(homedir,
1541                         curclass.homedir ? curclass.homedir :
1542                         "/");
1543                     if (EMPTYSTR(homedir))
1544                               homedir[0] = '/';
1545                     if (EMPTYSTR(root) || chroot(root) < 0) {
1546                               syslog(LOG_NOTICE,
1547                                   "GUEST user %s: can't chroot to %s: %m",
1548                                   pw->pw_name, root);
1549                               goto bad_guest;
1550                     }
1551                     if (chdir(homedir) < 0) {
1552                               syslog(LOG_NOTICE,
1553                                   "GUEST user %s: can't chdir to %s: %m",
1554                                   pw->pw_name, homedir);
1555  bad_guest:
1556                               fatal("Can't set guest privileges.");
1557                     }
1558                     break;
1559           case CLASS_CHROOT:
1560                     format_path(root,
1561                         curclass.chroot ? curclass.chroot :
1562                         pw->pw_dir);
1563                     format_path(homedir,
1564                         curclass.homedir ? curclass.homedir :
1565                         "/");
1566                     if (EMPTYSTR(homedir))
1567                               homedir[0] = '/';
1568                     if (EMPTYSTR(root) || chroot(root) < 0) {
1569                               syslog(LOG_NOTICE,
1570                                   "CHROOT user %s: can't chroot to %s: %m",
1571                                   pw->pw_name, root);
1572                               goto bad_chroot;
1573                     }
1574                     if (chdir(homedir) < 0) {
1575                               syslog(LOG_NOTICE,
1576                                   "CHROOT user %s: can't chdir to %s: %m",
1577                                   pw->pw_name, homedir);
1578  bad_chroot:
1579                               fatal("Can't change root.");
1580                     }
1581                     break;
1582           case CLASS_REAL:
1583                               /* only chroot REAL if explicitly requested */
1584                     if (! EMPTYSTR(curclass.chroot)) {
1585                               format_path(root, curclass.chroot);
1586                               if (EMPTYSTR(root) || chroot(root) < 0) {
1587                                         syslog(LOG_NOTICE,
1588                                             "REAL user %s: can't chroot to %s: %m",
1589                                             pw->pw_name, root);
1590                                         goto bad_chroot;
1591                               }
1592                     }
1593                     format_path(homedir,
1594                         curclass.homedir ? curclass.homedir :
1595                         pw->pw_dir);
1596                     if (EMPTYSTR(homedir) || chdir(homedir) < 0) {
1597                               if (chdir("/") < 0) {
1598                                         syslog(LOG_NOTICE,
1599                                             "REAL user %s: can't chdir to %s: %m",
1600                                             pw->pw_name,
1601                                             !EMPTYSTR(homedir) ?  homedir : "/");
1602                                         reply(530,
1603                                             "User %s: can't change directory to %s.",
1604                                             pw->pw_name,
1605                                             !EMPTYSTR(homedir) ? homedir : "/");
1606                                         goto bad;
1607                               } else {
1608                                         reply(-230,
1609                                             "No directory! Logging in with home=/");
1610                                         homedir[0] = '/';
1611                               }
1612                     }
1613                     break;
1614           }
1615 #ifndef LOGIN_CAP
1616           setsid();
1617           setlogin(pw->pw_name);
1618 #endif
1619           if (dropprivs ||
1620               (curclass.type != CLASS_REAL &&
1621               ntohs(ctrl_addr.su_port) > IPPORT_RESERVED + 1)) {
1622                     dropprivs++;
1623                     if (setgid((gid_t)pw->pw_gid) < 0) {
1624                               syslog(LOG_NOTICE, "user %s: can't setgid: %m", pw->pw_name);
1625                               fatal("Can't drop privileges.");
1626                     }
1627                     if (setuid((uid_t)pw->pw_uid) < 0) {
1628                               syslog(LOG_NOTICE, "user %s: can't setuid: %m", pw->pw_name);
1629                               fatal("Can't drop privileges.");
1630                     }
1631           } else {
1632                     if (seteuid((uid_t)pw->pw_uid) < 0) {
1633                               syslog(LOG_NOTICE, "user %s: can't seteuid: %m", pw->pw_name);
1634                               fatal("Can't drop privileges.");
1635                     }
1636           }
1637           setenv("HOME", homedir, 1);
1638 
1639           if (curclass.type == CLASS_GUEST && passwd[0] == '-')
1640                     quietmessages = 1;
1641 
1642                               /*
1643                                * Display a login message, if it exists.
1644                                * N.B. reply(230,) must follow the message.
1645                                */
1646           if (! EMPTYSTR(curclass.motd))
1647                     (void)display_file(conffilename(curclass.motd), 230);
1648           show_chdir_messages(230);
1649           if (curclass.type == CLASS_GUEST) {
1650                     char *p;
1651 
1652                     reply(230, "Guest login ok, access restrictions apply.");
1653 #if defined(HAVE_SETPROCTITLE)
1654                     snprintf(proctitle, sizeof(proctitle),
1655                         "%s: anonymous/%s", remotehost, passwd);
1656                     setproctitle("%s", proctitle);
1657 #endif /* defined(HAVE_SETPROCTITLE) */
1658                     if (logging)
1659                               syslog(LOG_INFO,
1660                               "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1661                                   remoteloghost, passwd,
1662                                   curclass.classname, CURCLASSTYPE);
1663                               /* store guest password reply into pw_passwd */
1664                     REASSIGN(pw->pw_passwd, ftpd_strdup(passwd));
1665                     for (p = pw->pw_passwd; *p; p++)
1666                               if (!isgraph((unsigned char)*p))
1667                                         *p = '_';
1668           } else {
1669                     reply(230, "User %s logged in.", pw->pw_name);
1670 #if defined(HAVE_SETPROCTITLE)
1671                     snprintf(proctitle, sizeof(proctitle),
1672                         "%s: %s", remotehost, pw->pw_name);
1673                     setproctitle("%s", proctitle);
1674 #endif /* defined(HAVE_SETPROCTITLE) */
1675                     if (logging)
1676                               syslog(LOG_INFO,
1677                                   "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1678                                   remoteloghost, pw->pw_name,
1679                                   curclass.classname, CURCLASSTYPE);
1680           }
1681           (void) umask(curclass.umask);
1682 #ifdef    LOGIN_CAP
1683           login_close(lc);
1684 #endif
1685           return;
1686 
1687  bad:
1688 #ifdef    LOGIN_CAP
1689           login_close(lc);
1690 #endif
1691                               /* Forget all about it... */
1692           end_login();
1693 }
1694 
1695 void
retrieve(const char * argv[],const char * name)1696 retrieve(const char *argv[], const char *name)
1697 {
1698           FILE *fin, *dout;
1699           struct stat st;
1700           int (*closefunc)(FILE *) = NULL;
1701           int dolog, sendrv, closerv, stderrfd, isconversion, isdata, isls;
1702           struct timeval start, finish, td, *tdp;
1703           struct rusage rusage_before, rusage_after;
1704           const char *dispname;
1705           const char *error;
1706 
1707           sendrv = closerv = stderrfd = -1;
1708           isconversion = isdata = isls = dolog = 0;
1709           tdp = NULL;
1710           dispname = name;
1711           fin = dout = NULL;
1712           error = NULL;
1713           if (argv == NULL) {           /* if not running a command ... */
1714                     dolog = 1;
1715                     isdata = 1;
1716                     fin = fopen(name, "r");
1717                     closefunc = fclose;
1718                     if (fin == NULL)    /* doesn't exist?; try a conversion */
1719                               argv = do_conversion(name);
1720                     if (argv != NULL) {
1721                               isconversion++;
1722                               syslog(LOG_DEBUG, "get command: '%s' on '%s'",
1723                                   argv[0], name);
1724                     }
1725           }
1726           if (argv != NULL) {
1727                     char temp[MAXPATHLEN];
1728 
1729                     if (strcmp(argv[0], INTERNAL_LS) == 0) {
1730                               isls = 1;
1731                               stderrfd = -1;
1732                     } else {
1733                               (void)snprintf(temp, sizeof(temp), "%s", TMPFILE);
1734                               stderrfd = mkstemp(temp);
1735                               if (stderrfd != -1)
1736                                         (void)unlink(temp);
1737                     }
1738                     dispname = argv[0];
1739                     fin = ftpd_popen(argv, "r", stderrfd);
1740                     closefunc = ftpd_pclose;
1741                     st.st_size = -1;
1742                     st.st_blksize = BUFSIZ;
1743           }
1744           if (fin == NULL) {
1745                     if (errno != 0) {
1746                               perror_reply(550, dispname);
1747                               if (dolog)
1748                                         logxfer("get", -1, name, NULL, NULL,
1749                                             strerror(errno));
1750                     }
1751                     goto cleanupretrieve;
1752           }
1753           byte_count = -1;
1754           if (argv == NULL
1755               && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1756                     error = "Not a plain file";
1757                     reply(550, "%s: %s.", dispname, error);
1758                     goto done;
1759           }
1760           if (restart_point) {
1761                     if (type == TYPE_A) {
1762                               off_t i;
1763                               int c;
1764 
1765                               for (i = 0; i < restart_point; i++) {
1766                                         if ((c=getc(fin)) == EOF) {
1767                                                   error = strerror(errno);
1768                                                   perror_reply(550, dispname);
1769                                                   goto done;
1770                                         }
1771                                         if (c == '\n')
1772                                                   i++;
1773                               }
1774                     } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1775                               error = strerror(errno);
1776                               perror_reply(550, dispname);
1777                               goto done;
1778                     }
1779           }
1780           dout = dataconn(dispname, st.st_size, "w");
1781           if (dout == NULL)
1782                     goto done;
1783 
1784           (void)getrusage(RUSAGE_SELF, &rusage_before);
1785           (void)gettimeofday(&start, NULL);
1786           sendrv = send_data(fin, dout, &st, isdata);
1787           (void)gettimeofday(&finish, NULL);
1788           (void)getrusage(RUSAGE_SELF, &rusage_after);
1789           closedataconn(dout);                    /* close now to affect timing stats */
1790           timersub(&finish, &start, &td);
1791           tdp = &td;
1792  done:
1793           if (dolog) {
1794                     logxfer("get", byte_count, name, NULL, tdp, error);
1795                     if (tdp != NULL)
1796                               logrusage(&rusage_before, &rusage_after);
1797           }
1798           closerv = (*closefunc)(fin);
1799           if (sendrv == 0) {
1800                     FILE *errf;
1801                     struct stat sb;
1802 
1803                     if (!isls && argv != NULL && closerv != 0) {
1804                               reply(-226,
1805                                   "Command returned an exit status of %d",
1806                                   closerv);
1807                               if (isconversion)
1808                                         syslog(LOG_WARNING,
1809                                             "retrieve command: '%s' returned %d",
1810                                             argv[0], closerv);
1811                     }
1812                     if (!isls && argv != NULL && stderrfd != -1 &&
1813                         (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 &&
1814                         ((errf = fdopen(stderrfd, "r")) != NULL)) {
1815                               char *cp, line[LINE_MAX];
1816 
1817                               reply(-226, "Command error messages:");
1818                               rewind(errf);
1819                               while (fgets(line, sizeof(line), errf) != NULL) {
1820                                         if ((cp = strchr(line, '\n')) != NULL)
1821                                                   *cp = '\0';
1822                                         reply(0, "  %s", line);
1823                               }
1824                               (void) fflush(stdout);
1825                               (void) fclose(errf);
1826                                         /* a reply(226,) must follow */
1827                     }
1828                     reply(226, "Transfer complete.");
1829           }
1830  cleanupretrieve:
1831           if (stderrfd != -1)
1832                     (void)close(stderrfd);
1833           if (isconversion)
1834                     free(argv);
1835 }
1836 
1837 void
store(const char * name,const char * fmode,int unique)1838 store(const char *name, const char *fmode, int unique)
1839 {
1840           FILE *fout, *din;
1841           struct stat st;
1842           int (*closefunc)(FILE *);
1843           struct timeval start, finish, td, *tdp;
1844           const char *desc, *error;
1845 
1846           din = NULL;
1847           desc = (*fmode == 'w') ? "put" : "append";
1848           error = NULL;
1849           if (unique && stat(name, &st) == 0 &&
1850               (name = gunique(name)) == NULL) {
1851                     logxfer(desc, -1, name, NULL, NULL,
1852                         "cannot create unique file");
1853                     goto cleanupstore;
1854           }
1855 
1856           if (restart_point)
1857                     fmode = "r+";
1858           fout = fopen(name, fmode);
1859           closefunc = fclose;
1860           tdp = NULL;
1861           if (fout == NULL) {
1862                     perror_reply(553, name);
1863                     logxfer(desc, -1, name, NULL, NULL, strerror(errno));
1864                     goto cleanupstore;
1865           }
1866           byte_count = -1;
1867           if (restart_point) {
1868                     if (type == TYPE_A) {
1869                               off_t i;
1870                               int c;
1871 
1872                               for (i = 0; i < restart_point; i++) {
1873                                         if ((c=getc(fout)) == EOF) {
1874                                                   error = strerror(errno);
1875                                                   perror_reply(550, name);
1876                                                   goto done;
1877                                         }
1878                                         if (c == '\n')
1879                                                   i++;
1880                               }
1881                               /*
1882                                * We must do this seek to "current" position
1883                                * because we are changing from reading to
1884                                * writing.
1885                                */
1886                               if (fseek(fout, 0L, SEEK_CUR) < 0) {
1887                                         error = strerror(errno);
1888                                         perror_reply(550, name);
1889                                         goto done;
1890                               }
1891                     } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1892                               error = strerror(errno);
1893                               perror_reply(550, name);
1894                               goto done;
1895                     }
1896           }
1897           din = dataconn(name, (off_t)-1, "r");
1898           if (din == NULL)
1899                     goto done;
1900           (void)gettimeofday(&start, NULL);
1901           if (receive_data(din, fout) == 0) {
1902                     if (unique)
1903                               reply(226, "Transfer complete (unique file name:%s).",
1904                                   name);
1905                     else
1906                               reply(226, "Transfer complete.");
1907           }
1908           (void)gettimeofday(&finish, NULL);
1909           closedataconn(din);           /* close now to affect timing stats */
1910           timersub(&finish, &start, &td);
1911           tdp = &td;
1912  done:
1913           logxfer(desc, byte_count, name, NULL, tdp, error);
1914           (*closefunc)(fout);
1915  cleanupstore:
1916           ;
1917 }
1918 
1919 static FILE *
getdatasock(const char * fmode)1920 getdatasock(const char *fmode)
1921 {
1922           int                 on, s, t, tries;
1923           in_port_t port;
1924 
1925           on = 1;
1926           if (data >= 0)
1927                     return (fdopen(data, fmode));
1928           if (! dropprivs) {
1929                     if (seteuid((uid_t)0) < 0) {
1930                               syslog(LOG_NOTICE, "getdatasock: can't seteuid 0: %m");
1931                               fatal("Can't reset privileges.");
1932                     }
1933           }
1934           s = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
1935           if (s < 0)
1936                     goto bad;
1937           if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1938               (char *) &on, sizeof(on)) < 0)
1939                     goto bad;
1940           if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1941               (char *) &on, sizeof(on)) < 0)
1942                     goto bad;
1943                               /* anchor socket to avoid multi-homing problems */
1944           data_source = ctrl_addr;
1945                               /*
1946                                * By default source port for PORT connctions is
1947                                * ctrlport-1 (see RFC959 section 5.2).
1948                                * However, if privs have been dropped and that
1949                                * would be < IPPORT_RESERVED, use a random port
1950                                * instead.
1951                                */
1952           if (dataport)
1953                     port = dataport;
1954           else
1955                     port = ntohs(ctrl_addr.su_port) - 1;
1956           if (dropprivs && port < IPPORT_RESERVED)
1957                     port = 0;           /* use random port */
1958           data_source.su_port = htons(port);
1959 
1960           for (tries = 1; ; tries++) {
1961                     if (bind(s, (struct sockaddr *)&data_source.si_su,
1962                         data_source.su_len) >= 0)
1963                               break;
1964                     if (errno != EADDRINUSE || tries > 10)
1965                               goto bad;
1966                     sleep(tries);
1967           }
1968           if (! dropprivs) {
1969                     if (seteuid((uid_t)pw->pw_uid) < 0) {
1970                               syslog(LOG_NOTICE, "user %s: can't seteuid: %m", pw->pw_name);
1971                               fatal("Can't drop privileges.");
1972                     }
1973           }
1974 #ifdef IP_TOS
1975           if (!mapped && ctrl_addr.su_family == AF_INET) {
1976                     on = IPTOS_THROUGHPUT;
1977                     if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on,
1978                                      sizeof(int)) < 0)
1979                               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1980           }
1981 #endif
1982           return (fdopen(s, fmode));
1983  bad:
1984                     /* Return the real value of errno (close may change it) */
1985           t = errno;
1986           if (! dropprivs) {
1987                     if (seteuid((uid_t)pw->pw_uid) < 0) {
1988                               syslog(LOG_NOTICE, "user %s: can't seteuid: %m", pw->pw_name);
1989                               fatal("Can't drop privileges.");
1990                     }
1991           }
1992           if (s >= 0)
1993                     (void) close(s);
1994           errno = t;
1995           return (NULL);
1996 }
1997 
1998 FILE *
dataconn(const char * name,off_t size,const char * fmode)1999 dataconn(const char *name, off_t size, const char *fmode)
2000 {
2001           char sizebuf[32];
2002           FILE *file;
2003           int retry, tos, keepalive, conerrno;
2004 
2005           file_size = size;
2006           byte_count = 0;
2007           if (size != (off_t) -1)
2008                     (void)snprintf(sizebuf, sizeof(sizebuf), " (" LLF " byte%s)",
2009                         (LLT)size, PLURAL(size));
2010           else
2011                     sizebuf[0] = '\0';
2012           if (pdata >= 0) {
2013                     struct sockinet from;
2014                     int s;
2015                     socklen_t fromlen = sizeof(from.su_len);
2016 
2017                     (void) alarm(curclass.timeout);
2018                     s = accept(pdata, (struct sockaddr *)&from.si_su, &fromlen);
2019                     (void) alarm(0);
2020                     if (s < 0) {
2021                               reply(425, "Can't open data connection.");
2022                               (void) close(pdata);
2023                               pdata = -1;
2024                               return (NULL);
2025                     }
2026                     (void) close(pdata);
2027                     pdata = s;
2028                     switch (from.su_family) {
2029                     case AF_INET:
2030 #ifdef IP_TOS
2031                               if (!mapped) {
2032                                         tos = IPTOS_THROUGHPUT;
2033                                         (void) setsockopt(s, IPPROTO_IP, IP_TOS,
2034                                             (char *)&tos, sizeof(int));
2035                               }
2036                               break;
2037 #endif
2038                     }
2039                     /* Set keepalives on the socket to detect dropped conns. */
2040 #ifdef SO_KEEPALIVE
2041                     keepalive = 1;
2042                     (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
2043                         (char *)&keepalive, sizeof(int));
2044 #endif
2045                     reply(150, "Opening %s mode data connection for '%s'%s.",
2046                          type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
2047                     return (fdopen(pdata, fmode));
2048           }
2049           if (data >= 0) {
2050                     reply(125, "Using existing data connection for '%s'%s.",
2051                         name, sizebuf);
2052                     usedefault = 1;
2053                     return (fdopen(data, fmode));
2054           }
2055           if (usedefault)
2056                     data_dest = his_addr;
2057           usedefault = 1;
2058           retry = conerrno = 0;
2059           do {
2060                     file = getdatasock(fmode);
2061                     if (file == NULL) {
2062                               char hbuf[NI_MAXHOST];
2063                               char pbuf[NI_MAXSERV];
2064                               conerrno = errno;
2065                               if (getnameinfo((struct sockaddr *)&data_source.si_su,
2066                                   data_source.su_len, hbuf, sizeof(hbuf), pbuf,
2067                                   sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV))
2068                                         strlcpy(hbuf, "?", sizeof(hbuf));
2069                               reply(425, "Can't create data socket (%s,%s): %s.",
2070                                     hbuf, pbuf, strerror(conerrno));
2071                               return (NULL);
2072                     }
2073                     data = fileno(file);
2074                     conerrno = 0;
2075                     if (connect(data, (struct sockaddr *)&data_dest.si_su,
2076                         data_dest.su_len) == 0)
2077                               break;
2078                     conerrno = errno;
2079                     (void) fclose(file);
2080                     file = NULL;
2081                     data = -1;
2082                     if (conerrno == EADDRINUSE) {
2083                               sleep((unsigned) swaitint);
2084                               retry += swaitint;
2085                     } else {
2086                               break;
2087                     }
2088           } while (retry <= swaitmax);
2089           if (conerrno != 0) {
2090                     perror_reply(425, "Can't build data connection");
2091                     return (NULL);
2092           }
2093           reply(150, "Opening %s mode data connection for '%s'%s.",
2094                type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
2095           return (file);
2096 }
2097 
2098 void
closedataconn(FILE * fd)2099 closedataconn(FILE *fd)
2100 {
2101 
2102           if (fd == NULL)
2103                     return;
2104           (void)fclose(fd);
2105           data = -1;
2106           if (pdata >= 0)
2107                     (void)close(pdata);
2108           pdata = -1;
2109 }
2110 
2111 int
write_data(int fd,char * buf,size_t size,off_t * bufrem,struct timeval * then,int isdata)2112 write_data(int fd, char *buf, size_t size, off_t *bufrem,
2113     struct timeval *then, int isdata)
2114 {
2115           struct timeval now, td;
2116           ssize_t c;
2117 
2118           while (size > 0) {
2119                     c = size;
2120                     if (curclass.writesize) {
2121                               if (curclass.writesize < c)
2122                                         c = curclass.writesize;
2123                     }
2124                     if (curclass.rateget) {
2125                               if (*bufrem < c)
2126                                         c = *bufrem;
2127                     }
2128                     (void) alarm(curclass.timeout);
2129                     c = write(fd, buf, c);
2130                     if (c <= 0)
2131                               return (1);
2132                     buf += c;
2133                     size -= c;
2134                     byte_count += c;
2135                     if (isdata) {
2136                               total_data_out += c;
2137                               total_data += c;
2138                     }
2139                     total_bytes_out += c;
2140                     total_bytes += c;
2141                     if (curclass.rateget) {
2142                               *bufrem -= c;
2143                               if (*bufrem == 0) {
2144                                         (void)gettimeofday(&now, NULL);
2145                                         timersub(&now, then, &td);
2146                                         if (td.tv_sec == 0) {
2147                                                   usleep(1000000 - td.tv_usec);
2148                                                   (void)gettimeofday(then, NULL);
2149                                         } else
2150                                                   *then = now;
2151                                         *bufrem = curclass.rateget;
2152                               }
2153                     }
2154           }
2155           return (0);
2156 }
2157 
2158 static enum send_status
send_data_with_read(int filefd,int netfd,const struct stat * st,int isdata)2159 send_data_with_read(int filefd, int netfd, const struct stat *st, int isdata)
2160 {
2161           struct timeval then;
2162           off_t bufrem;
2163           ssize_t readsize;
2164           char *buf;
2165           int c, error;
2166 
2167           if (curclass.readsize > 0)
2168                     readsize = curclass.readsize;
2169           else
2170                     readsize = st->st_blksize;
2171           if ((buf = malloc(readsize)) == NULL) {
2172                     perror_reply(451, "Local resource failure: malloc");
2173                     return (SS_NO_TRANSFER);
2174           }
2175 
2176           if (curclass.rateget) {
2177                     bufrem = curclass.rateget;
2178                     (void)gettimeofday(&then, NULL);
2179           } else
2180                     bufrem = readsize;
2181           for (;;) {
2182                     (void) alarm(curclass.timeout);
2183                     c = read(filefd, buf, readsize);
2184                     if (c == 0)
2185                               error = SS_SUCCESS;
2186                     else if (c < 0)
2187                               error = SS_FILE_ERROR;
2188                     else if (write_data(netfd, buf, c, &bufrem, &then, isdata))
2189                               error = SS_DATA_ERROR;
2190                     else if (urgflag && handleoobcmd())
2191                               error = SS_ABORTED;
2192                     else
2193                               continue;
2194 
2195                     free(buf);
2196                     return (error);
2197           }
2198 }
2199 
2200 static enum send_status
send_data_with_mmap(int filefd,int netfd,const struct stat * st,int isdata)2201 send_data_with_mmap(int filefd, int netfd, const struct stat *st, int isdata)
2202 {
2203           struct timeval then;
2204           off_t bufrem, filesize, off, origoff;
2205           ssize_t mapsize, winsize;
2206           int error, sendbufsize, sendlowat;
2207           void *win;
2208 
2209           bufrem = 0;
2210           if (curclass.sendbufsize) {
2211                     sendbufsize = curclass.sendbufsize;
2212                     if (setsockopt(netfd, SOL_SOCKET, SO_SNDBUF,
2213                         &sendbufsize, sizeof(int)) == -1)
2214                               syslog(LOG_WARNING, "setsockopt(SO_SNDBUF, %d): %m",
2215                                   sendbufsize);
2216           }
2217 
2218           if (curclass.sendlowat) {
2219                     sendlowat = curclass.sendlowat;
2220                     if (setsockopt(netfd, SOL_SOCKET, SO_SNDLOWAT,
2221                         &sendlowat, sizeof(int)) == -1)
2222                               syslog(LOG_WARNING, "setsockopt(SO_SNDLOWAT, %d): %m",
2223                                   sendlowat);
2224           }
2225 
2226           winsize = curclass.mmapsize;
2227           filesize = st->st_size;
2228           if (ftpd_debug)
2229                     syslog(LOG_INFO, "mmapsize = " LLF ", writesize = " LLF,
2230                         (LLT)winsize, (LLT)curclass.writesize);
2231           if (winsize <= 0)
2232                     goto try_read;
2233 
2234           off = lseek(filefd, (off_t)0, SEEK_CUR);
2235           if (off == -1)
2236                     goto try_read;
2237 
2238           origoff = off;
2239           if (curclass.rateget) {
2240                     bufrem = curclass.rateget;
2241                     (void)gettimeofday(&then, NULL);
2242           } else
2243                     bufrem = winsize;
2244           while (1) {
2245                     mapsize = MIN(filesize - off, winsize);
2246                     if (mapsize == 0)
2247                               break;
2248                     win = mmap(NULL, mapsize, PROT_READ,
2249                         MAP_FILE|MAP_SHARED, filefd, off);
2250                     if (win == MAP_FAILED) {
2251                               if (off == origoff)
2252                                         goto try_read;
2253                               return (SS_FILE_ERROR);
2254                     }
2255                     (void) madvise(win, mapsize, MADV_SEQUENTIAL);
2256                     error = write_data(netfd, win, mapsize, &bufrem, &then,
2257                         isdata);
2258                     (void) madvise(win, mapsize, MADV_DONTNEED);
2259                     munmap(win, mapsize);
2260                     if (urgflag && handleoobcmd())
2261                               return (SS_ABORTED);
2262                     if (error)
2263                               return (SS_DATA_ERROR);
2264                     off += mapsize;
2265           }
2266           return (SS_SUCCESS);
2267 
2268  try_read:
2269           return (send_data_with_read(filefd, netfd, st, isdata));
2270 }
2271 
2272 /*
2273  * Transfer the contents of "instr" to "outstr" peer using the appropriate
2274  * encapsulation of the data subject to Mode, Structure, and Type.
2275  *
2276  * NB: Form isn't handled.
2277  */
2278 static int
send_data(FILE * instr,FILE * outstr,const struct stat * st,int isdata)2279 send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
2280 {
2281           int        c, filefd, netfd, rval;
2282 
2283           urgflag = 0;
2284           transflag = 1;
2285           rval = -1;
2286 
2287           switch (type) {
2288 
2289           case TYPE_A:
2290  /* XXXLUKEM: rate limit ascii send (get) */
2291                     (void) alarm(curclass.timeout);
2292                     while ((c = getc(instr)) != EOF) {
2293                               if (urgflag && handleoobcmd())
2294                                         goto cleanup_send_data;
2295                               byte_count++;
2296                               if (c == '\n') {
2297                                         if (ferror(outstr))
2298                                                   goto data_err;
2299                                         (void) putc('\r', outstr);
2300                                         if (isdata) {
2301                                                   total_data_out++;
2302                                                   total_data++;
2303                                         }
2304                                         total_bytes_out++;
2305                                         total_bytes++;
2306                               }
2307                               (void) putc(c, outstr);
2308                               if (isdata) {
2309                                         total_data_out++;
2310                                         total_data++;
2311                               }
2312                               total_bytes_out++;
2313                               total_bytes++;
2314                               if ((byte_count % 4096) == 0)
2315                                         (void) alarm(curclass.timeout);
2316                     }
2317                     (void) alarm(0);
2318                     fflush(outstr);
2319                     if (ferror(instr))
2320                               goto file_err;
2321                     if (ferror(outstr))
2322                               goto data_err;
2323                     rval = 0;
2324                     goto cleanup_send_data;
2325 
2326           case TYPE_I:
2327           case TYPE_L:
2328                     filefd = fileno(instr);
2329                     netfd = fileno(outstr);
2330                     switch (send_data_with_mmap(filefd, netfd, st, isdata)) {
2331 
2332                     case SS_SUCCESS:
2333                               break;
2334 
2335                     case SS_ABORTED:
2336                     case SS_NO_TRANSFER:
2337                               goto cleanup_send_data;
2338 
2339                     case SS_FILE_ERROR:
2340                               goto file_err;
2341 
2342                     case SS_DATA_ERROR:
2343                               goto data_err;
2344                     }
2345                     rval = 0;
2346                     goto cleanup_send_data;
2347 
2348           default:
2349                     reply(550, "Unimplemented TYPE %d in send_data", type);
2350                     goto cleanup_send_data;
2351           }
2352 
2353  data_err:
2354           (void) alarm(0);
2355           perror_reply(426, "Data connection");
2356           goto cleanup_send_data;
2357 
2358  file_err:
2359           (void) alarm(0);
2360           perror_reply(551, "Error on input file");
2361           goto cleanup_send_data;
2362 
2363  cleanup_send_data:
2364           (void) alarm(0);
2365           transflag = 0;
2366           urgflag = 0;
2367           if (isdata) {
2368                     total_files_out++;
2369                     total_files++;
2370           }
2371           total_xfers_out++;
2372           total_xfers++;
2373           return (rval);
2374 }
2375 
2376 /*
2377  * Transfer data from peer to "outstr" using the appropriate encapulation of
2378  * the data subject to Mode, Structure, and Type.
2379  *
2380  * N.B.: Form isn't handled.
2381  */
2382 static int
receive_data(FILE * instr,FILE * outstr)2383 receive_data(FILE *instr, FILE *outstr)
2384 {
2385           int       c, netfd, filefd, rval;
2386           int       volatile bare_lfs;
2387           off_t     byteswritten;
2388           char      *buf;
2389           ssize_t   readsize;
2390           struct sigaction sa, sa_saved;
2391           struct stat st;
2392 
2393           memset(&sa, 0, sizeof(sa));
2394           sigfillset(&sa.sa_mask);
2395           sa.sa_flags = SA_RESTART;
2396           sa.sa_handler = lostconn;
2397           (void) sigaction(SIGALRM, &sa, &sa_saved);
2398 
2399           bare_lfs = 0;
2400           urgflag = 0;
2401           transflag = 1;
2402           rval = -1;
2403           byteswritten = 0;
2404           buf = NULL;
2405 
2406 #define FILESIZECHECK(x) \
2407                               do { \
2408                                         if (curclass.maxfilesize != -1 && \
2409                                             (x) > curclass.maxfilesize) { \
2410                                                   errno = EFBIG; \
2411                                                   goto file_err; \
2412                                         } \
2413                               } while (0)
2414 
2415           switch (type) {
2416 
2417           case TYPE_I:
2418           case TYPE_L:
2419                     netfd = fileno(instr);
2420                     filefd = fileno(outstr);
2421                     (void) alarm(curclass.timeout);
2422                     if (curclass.readsize)
2423                               readsize = curclass.readsize;
2424                     else if (fstat(filefd, &st) != -1)
2425                               readsize = (ssize_t)st.st_blksize;
2426                     else
2427                               readsize = BUFSIZ;
2428                     if ((buf = malloc(readsize)) == NULL) {
2429                               perror_reply(451, "Local resource failure: malloc");
2430                               goto cleanup_recv_data;
2431                     }
2432                     if (curclass.rateput) {
2433                               while (1) {
2434                                         int d;
2435                                         struct timeval then, now, td;
2436                                         off_t bufrem;
2437 
2438                                         (void)gettimeofday(&then, NULL);
2439                                         errno = c = d = 0;
2440                                         for (bufrem = curclass.rateput; bufrem > 0; ) {
2441                                                   if ((c = read(netfd, buf,
2442                                                       MIN(readsize, bufrem))) <= 0)
2443                                                             goto recvdone;
2444                                                   if (urgflag && handleoobcmd())
2445                                                             goto cleanup_recv_data;
2446                                                   FILESIZECHECK(byte_count + c);
2447                                                   if ((d = write(filefd, buf, c)) != c)
2448                                                             goto file_err;
2449                                                   (void) alarm(curclass.timeout);
2450                                                   bufrem -= c;
2451                                                   byte_count += c;
2452                                                   total_data_in += c;
2453                                                   total_data += c;
2454                                                   total_bytes_in += c;
2455                                                   total_bytes += c;
2456                                         }
2457                                         (void)gettimeofday(&now, NULL);
2458                                         timersub(&now, &then, &td);
2459                                         if (td.tv_sec == 0)
2460                                                   usleep(1000000 - td.tv_usec);
2461                               }
2462                     } else {
2463                               while ((c = read(netfd, buf, readsize)) > 0) {
2464                                         if (urgflag && handleoobcmd())
2465                                                   goto cleanup_recv_data;
2466                                         FILESIZECHECK(byte_count + c);
2467                                         if (write(filefd, buf, c) != c)
2468                                                   goto file_err;
2469                                         (void) alarm(curclass.timeout);
2470                                         byte_count += c;
2471                                         total_data_in += c;
2472                                         total_data += c;
2473                                         total_bytes_in += c;
2474                                         total_bytes += c;
2475                               }
2476                     }
2477  recvdone:
2478                     if (c < 0)
2479                               goto data_err;
2480                     rval = 0;
2481                     goto cleanup_recv_data;
2482 
2483           case TYPE_E:
2484                     reply(553, "TYPE E not implemented.");
2485                     goto cleanup_recv_data;
2486 
2487           case TYPE_A:
2488                     (void) alarm(curclass.timeout);
2489  /* XXXLUKEM: rate limit ascii receive (put) */
2490                     while ((c = getc(instr)) != EOF) {
2491                               if (urgflag && handleoobcmd())
2492                                         goto cleanup_recv_data;
2493                               byte_count++;
2494                               total_data_in++;
2495                               total_data++;
2496                               total_bytes_in++;
2497                               total_bytes++;
2498                               if ((byte_count % 4096) == 0)
2499                                         (void) alarm(curclass.timeout);
2500                               if (c == '\n')
2501                                         bare_lfs++;
2502                               while (c == '\r') {
2503                                         if (ferror(outstr))
2504                                                   goto data_err;
2505                                         if ((c = getc(instr)) != '\n') {
2506                                                   byte_count++;
2507                                                   total_data_in++;
2508                                                   total_data++;
2509                                                   total_bytes_in++;
2510                                                   total_bytes++;
2511                                                   if ((byte_count % 4096) == 0)
2512                                                             (void) alarm(curclass.timeout);
2513                                                   byteswritten++;
2514                                                   FILESIZECHECK(byteswritten);
2515                                                   (void) putc ('\r', outstr);
2516                                                   if (c == '\0' || c == EOF)
2517                                                             goto contin2;
2518                                         }
2519                               }
2520                               byteswritten++;
2521                               FILESIZECHECK(byteswritten);
2522                               (void) putc(c, outstr);
2523  contin2: ;
2524                     }
2525                     (void) alarm(0);
2526                     fflush(outstr);
2527                     if (ferror(instr))
2528                               goto data_err;
2529                     if (ferror(outstr))
2530                               goto file_err;
2531                     if (bare_lfs) {
2532                               reply(-226,
2533                                   "WARNING! %d bare linefeeds received in ASCII mode",
2534                                   bare_lfs);
2535                               reply(0, "File may not have transferred correctly.");
2536                     }
2537                     rval = 0;
2538                     goto cleanup_recv_data;
2539 
2540           default:
2541                     reply(550, "Unimplemented TYPE %d in receive_data", type);
2542                     goto cleanup_recv_data;
2543           }
2544 #undef FILESIZECHECK
2545 
2546  data_err:
2547           (void) alarm(0);
2548           perror_reply(426, "Data Connection");
2549           goto cleanup_recv_data;
2550 
2551  file_err:
2552           (void) alarm(0);
2553           perror_reply(452, "Error writing file");
2554           goto cleanup_recv_data;
2555 
2556  cleanup_recv_data:
2557           (void) alarm(0);
2558           (void) sigaction(SIGALRM, &sa_saved, NULL);
2559           if (buf)
2560                     free(buf);
2561           transflag = 0;
2562           urgflag = 0;
2563           total_files_in++;
2564           total_files++;
2565           total_xfers_in++;
2566           total_xfers++;
2567           return (rval);
2568 }
2569 
2570 void
statcmd(void)2571 statcmd(void)
2572 {
2573           struct sockinet *su = NULL;
2574           static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
2575           unsigned char *a, *p;
2576           int ispassive, af;
2577           off_t otbi, otbo, otb;
2578 
2579           a = p = NULL;
2580 
2581           reply(-211, "%s FTP server status:", hostname);
2582           reply(0, "Version: %s", EMPTYSTR(version) ? "<suppressed>" : version);
2583           hbuf[0] = '\0';
2584           if (!getnameinfo((struct sockaddr *)&his_addr.si_su, his_addr.su_len,
2585                               hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)
2586               && strcmp(remotehost, hbuf) != 0)
2587                     reply(0, "Connected to %s (%s)", remotehost, hbuf);
2588           else
2589                     reply(0, "Connected to %s", remotehost);
2590 
2591           if (logged_in) {
2592                     if (curclass.type == CLASS_GUEST)
2593                               reply(0, "Logged in anonymously");
2594                     else
2595                               reply(0, "Logged in as %s%s", pw->pw_name,
2596                                   curclass.type == CLASS_CHROOT ? " (chroot)" : "");
2597           } else if (askpasswd)
2598                     reply(0, "Waiting for password");
2599           else
2600                     reply(0, "Waiting for user name");
2601           cprintf(stdout, "    TYPE: %s", typenames[type]);
2602           if (type == TYPE_A || type == TYPE_E)
2603                     cprintf(stdout, ", FORM: %s", formnames[form]);
2604           if (type == TYPE_L) {
2605 #if NBBY == 8
2606                     cprintf(stdout, " %d", NBBY);
2607 #else
2608                               /* XXX: `bytesize' needs to be defined in this case */
2609                     cprintf(stdout, " %d", bytesize);
2610 #endif
2611           }
2612           cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
2613               strunames[stru], modenames[mode]);
2614           ispassive = 0;
2615           if (data != -1) {
2616                     reply(0, "Data connection open");
2617                     su = NULL;
2618           } else if (pdata != -1) {
2619                     reply(0, "in Passive mode");
2620                     if (curclass.advertise.su_len != 0)
2621                               su = &curclass.advertise;
2622                     else
2623                               su = &pasv_addr;
2624                     ispassive = 1;
2625                     goto printaddr;
2626           } else if (usedefault == 0) {
2627                     su = (struct sockinet *)&data_dest;
2628 
2629                     if (epsvall) {
2630                               reply(0, "EPSV only mode (EPSV ALL)");
2631                               goto epsvonly;
2632                     }
2633  printaddr:
2634                                                                       /* PASV/PORT */
2635                     if (su->su_family == AF_INET) {
2636                               a = (unsigned char *) &su->su_addr;
2637                               p = (unsigned char *) &su->su_port;
2638 #define UC(b) (((int) b) & 0xff)
2639                               reply(0, "%s (%d,%d,%d,%d,%d,%d)",
2640                                         ispassive ? "PASV" : "PORT" ,
2641                                         UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2642                                         UC(p[0]), UC(p[1]));
2643                     }
2644 
2645                                                                       /* LPSV/LPRT */
2646               {
2647                     int alen, i;
2648 
2649                     alen = 0;
2650                     switch (su->su_family) {
2651                     case AF_INET:
2652                               a = (unsigned char *) &su->su_addr;
2653                               p = (unsigned char *) &su->su_port;
2654                               alen = sizeof(su->su_addr);
2655                               af = 4;
2656                               break;
2657 #ifdef INET6
2658                     case AF_INET6:
2659                               a = (unsigned char *) &su->su_6addr;
2660                               p = (unsigned char *) &su->su_port;
2661                               alen = sizeof(su->su_6addr);
2662                               af = 6;
2663                               break;
2664 #endif
2665                     default:
2666                               af = 0;
2667                               break;
2668                     }
2669                     if (af) {
2670                               cprintf(stdout, "    %s (%d,%d",
2671                                   ispassive ? "LPSV" : "LPRT", af, alen);
2672                               for (i = 0; i < alen; i++)
2673                                         cprintf(stdout, ",%d", UC(a[i]));
2674                               cprintf(stdout, ",%d,%d,%d)\r\n",
2675                                   2, UC(p[0]), UC(p[1]));
2676 #undef UC
2677                     }
2678               }
2679 
2680                     /* EPRT/EPSV */
2681  epsvonly:
2682                     af = af2epsvproto(su->su_family);
2683                     hbuf[0] = '\0';
2684                     if (af > 0) {
2685                               struct sockinet tmp;
2686 
2687                               tmp = *su;
2688 #ifdef INET6
2689                               if (tmp.su_family == AF_INET6)
2690                                         tmp.su_scope_id = 0;
2691 #endif
2692                               if (getnameinfo((struct sockaddr *)&tmp.si_su,
2693                                   tmp.su_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
2694                                   NI_NUMERICHOST | NI_NUMERICSERV) == 0)
2695                                         reply(0, "%s (|%d|%s|%s|)",
2696                                             ispassive ? "EPSV" : "EPRT",
2697                                             af, hbuf, sbuf);
2698                     }
2699           } else
2700                     reply(0, "No data connection");
2701 
2702           if (logged_in) {
2703                     reply(0,
2704                         "Data sent:        " LLF " byte%s in " LLF " file%s",
2705                         (LLT)total_data_out, PLURAL(total_data_out),
2706                         (LLT)total_files_out, PLURAL(total_files_out));
2707                     reply(0,
2708                         "Data received:    " LLF " byte%s in " LLF " file%s",
2709                         (LLT)total_data_in, PLURAL(total_data_in),
2710                         (LLT)total_files_in, PLURAL(total_files_in));
2711                     reply(0,
2712                         "Total data:       " LLF " byte%s in " LLF " file%s",
2713                         (LLT)total_data, PLURAL(total_data),
2714                         (LLT)total_files, PLURAL(total_files));
2715           }
2716           otbi = total_bytes_in;
2717           otbo = total_bytes_out;
2718           otb = total_bytes;
2719           reply(0, "Traffic sent:     " LLF " byte%s in " LLF " transfer%s",
2720               (LLT)otbo, PLURAL(otbo),
2721               (LLT)total_xfers_out, PLURAL(total_xfers_out));
2722           reply(0, "Traffic received: " LLF " byte%s in " LLF " transfer%s",
2723               (LLT)otbi, PLURAL(otbi),
2724               (LLT)total_xfers_in, PLURAL(total_xfers_in));
2725           reply(0, "Total traffic:    " LLF " byte%s in " LLF " transfer%s",
2726               (LLT)otb, PLURAL(otb),
2727               (LLT)total_xfers, PLURAL(total_xfers));
2728 
2729           if (logged_in && !CURCLASS_FLAGS_ISSET(private)) {
2730                     struct ftpconv *cp;
2731 
2732                     reply(0, "%s", "");
2733                     reply(0, "Class: %s, type: %s",
2734                         curclass.classname, CURCLASSTYPE);
2735                     reply(0, "Check PORT/LPRT commands: %sabled",
2736                         CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
2737                     if (! EMPTYSTR(curclass.display))
2738                               reply(0, "Display file: %s", curclass.display);
2739                     if (! EMPTYSTR(curclass.notify))
2740                               reply(0, "Notify fileglob: %s", curclass.notify);
2741                     reply(0, "Idle timeout: " LLF ", maximum timeout: " LLF,
2742                         (LLT)curclass.timeout, (LLT)curclass.maxtimeout);
2743                     reply(0, "Current connections: %d", connections);
2744                     if (curclass.limit == -1)
2745                               reply(0, "Maximum connections: unlimited");
2746                     else
2747                               reply(0, "Maximum connections: " LLF,
2748                                   (LLT)curclass.limit);
2749                     if (curclass.limitfile)
2750                               reply(0, "Connection limit exceeded message file: %s",
2751                                   conffilename(curclass.limitfile));
2752                     if (! EMPTYSTR(curclass.chroot))
2753                               reply(0, "Chroot format: %s", curclass.chroot);
2754                     reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2755                         CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis");
2756                     if (! EMPTYSTR(curclass.homedir))
2757                               reply(0, "Homedir format: %s", curclass.homedir);
2758                     if (curclass.maxfilesize == -1)
2759                               reply(0, "Maximum file size: unlimited");
2760                     else
2761                               reply(0, "Maximum file size: " LLF,
2762                                   (LLT)curclass.maxfilesize);
2763                     if (! EMPTYSTR(curclass.motd))
2764                               reply(0, "MotD file: %s", conffilename(curclass.motd));
2765                     reply(0,
2766               "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2767                         CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis");
2768                     reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2769                         CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis");
2770                     reply(0, "Sanitize file names: %sabled",
2771                         CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis");
2772                     reply(0, "PASV/LPSV/EPSV connections: %sabled",
2773                         CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis");
2774                     if (curclass.advertise.su_len != 0) {
2775                               char buf[50];       /* big enough for IPv6 address */
2776                               const char *bp;
2777 
2778                               bp = inet_ntop(curclass.advertise.su_family,
2779                                   (void *)&curclass.advertise.su_addr,
2780                                   buf, sizeof(buf));
2781                               if (bp != NULL)
2782                                         reply(0, "PASV advertise address: %s", bp);
2783                     }
2784                     if (curclass.portmin && curclass.portmax)
2785                               reply(0, "PASV port range: " LLF " - " LLF,
2786                                   (LLT)curclass.portmin, (LLT)curclass.portmax);
2787                     if (curclass.rateget)
2788                               reply(0, "Rate get limit: " LLF " bytes/sec",
2789                                   (LLT)curclass.rateget);
2790                     else
2791                               reply(0, "Rate get limit: disabled");
2792                     if (curclass.rateput)
2793                               reply(0, "Rate put limit: " LLF " bytes/sec",
2794                                   (LLT)curclass.rateput);
2795                     else
2796                               reply(0, "Rate put limit: disabled");
2797                     if (curclass.mmapsize)
2798                               reply(0, "Mmap size: " LLF, (LLT)curclass.mmapsize);
2799                     else
2800                               reply(0, "Mmap size: disabled");
2801                     if (curclass.readsize)
2802                               reply(0, "Read size: " LLF, (LLT)curclass.readsize);
2803                     else
2804                               reply(0, "Read size: default");
2805                     if (curclass.writesize)
2806                               reply(0, "Write size: " LLF, (LLT)curclass.writesize);
2807                     else
2808                               reply(0, "Write size: default");
2809                     if (curclass.recvbufsize)
2810                               reply(0, "Receive buffer size: " LLF,
2811                                   (LLT)curclass.recvbufsize);
2812                     else
2813                               reply(0, "Receive buffer size: default");
2814                     if (curclass.sendbufsize)
2815                               reply(0, "Send buffer size: " LLF,
2816                                   (LLT)curclass.sendbufsize);
2817                     else
2818                               reply(0, "Send buffer size: default");
2819                     if (curclass.sendlowat)
2820                               reply(0, "Send low water mark: " LLF,
2821                                   (LLT)curclass.sendlowat);
2822                     else
2823                               reply(0, "Send low water mark: default");
2824                     reply(0, "Umask: %.04o", curclass.umask);
2825                     for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
2826                               if (cp->suffix == NULL || cp->types == NULL ||
2827                                   cp->command == NULL)
2828                                         continue;
2829                               reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2830                                   cp->suffix, cp->types, cp->disable, cp->command);
2831                     }
2832           }
2833 
2834           reply(211, "End of status");
2835 }
2836 
2837 void
fatal(const char * s)2838 fatal(const char *s)
2839 {
2840 
2841           reply(451, "Error in server: %s\n", s);
2842           reply(221, "Closing connection due to server error.");
2843           dologout(0);
2844           /* NOTREACHED */
2845 }
2846 
2847 /*
2848  * reply() --
2849  *        depending on the value of n, display fmt with a trailing CRLF and
2850  *        prefix of:
2851  *        n < -1              prefix the message with abs(n) + "-"    (initial line)
2852  *        n == 0              prefix the message with 4 spaces        (middle lines)
2853  *        n >  0              prefix the message with n + " "                   (final line)
2854  */
2855 void
reply(int n,const char * fmt,...)2856 reply(int n, const char *fmt, ...)
2857 {
2858           char      msg[MAXPATHLEN * 2 + 100];
2859           size_t    b;
2860           va_list   ap;
2861 
2862           if (n == 0)
2863                     b = snprintf(msg, sizeof(msg), "    ");
2864           else if (n < 0)
2865                     b = snprintf(msg, sizeof(msg), "%d-", -n);
2866           else
2867                     b = snprintf(msg, sizeof(msg), "%d ", n);
2868           va_start(ap, fmt);
2869           vsnprintf(msg + b, sizeof(msg) - b, fmt, ap);
2870           va_end(ap);
2871           cprintf(stdout, "%s\r\n", msg);
2872           (void)fflush(stdout);
2873           if (ftpd_debug)
2874                     syslog(LOG_DEBUG, "<--- %s", msg);
2875 }
2876 
2877 static void
logremotehost(struct sockinet * who)2878 logremotehost(struct sockinet *who)
2879 {
2880 
2881 #if defined(HAVE_SOCKADDR_SNPRINTF)
2882           char abuf[MAXHOSTNAMELEN];
2883 #endif
2884 
2885           struct sockaddr *sa = (struct sockaddr *)&who->si_su;
2886           if (getnameinfo(sa, who->su_len, remotehost, sizeof(remotehost), NULL,
2887               0, getnameopts))
2888                     strlcpy(remotehost, "?", sizeof(remotehost));
2889 #if defined(HAVE_SOCKADDR_SNPRINTF)
2890           sockaddr_snprintf(abuf, sizeof(abuf), "%a", sa);
2891           snprintf(remoteloghost, sizeof(remoteloghost), "%s(%s)", remotehost,
2892               abuf);
2893 #else
2894           strlcpy(remoteloghost, remotehost, sizeof(remoteloghost));
2895 #endif
2896 
2897 #if defined(HAVE_SETPROCTITLE)
2898           snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
2899           setproctitle("%s", proctitle);
2900 #endif /* defined(HAVE_SETPROCTITLE) */
2901           if (logging)
2902                     syslog(LOG_INFO, "connection from %s to %s",
2903                         remoteloghost, hostname);
2904 }
2905 
2906 /*
2907  * Record logout in wtmp file and exit with supplied status.
2908  * NOTE: because this is called from signal handlers it cannot
2909  *       use stdio (or call other functions that use stdio).
2910  */
2911 void
dologout(int status)2912 dologout(int status)
2913 {
2914           /*
2915           * Prevent reception of SIGURG from resulting in a resumption
2916           * back to the main program loop.
2917           */
2918           transflag = 0;
2919           logout_utmp();
2920           if (logged_in) {
2921 #ifdef KERBEROS
2922                     if (!notickets && krbtkfile_env)
2923                               unlink(krbtkfile_env);
2924 #endif
2925           }
2926           /* beware of flushing buffers after a SIGPIPE */
2927           if (xferlogfd != -1)
2928                     close(xferlogfd);
2929           _exit(status);
2930 }
2931 
2932 void
abor(void)2933 abor(void)
2934 {
2935 
2936           if (!transflag)
2937                     return;
2938           tmpline[0] = '\0';
2939           is_oob = 0;
2940           reply(426, "Transfer aborted. Data connection closed.");
2941           reply(226, "Abort successful");
2942           transflag = 0;                /* flag that the transfer has aborted */
2943 }
2944 
2945 void
statxfer(void)2946 statxfer(void)
2947 {
2948 
2949           if (!transflag)
2950                     return;
2951           tmpline[0] = '\0';
2952           is_oob = 0;
2953           if (file_size != (off_t) -1)
2954                     reply(213,
2955                         "Status: " LLF " of " LLF " byte%s transferred",
2956                         (LLT)byte_count, (LLT)file_size,
2957                         PLURAL(byte_count));
2958           else
2959                     reply(213, "Status: " LLF " byte%s transferred",
2960                         (LLT)byte_count, PLURAL(byte_count));
2961 }
2962 
2963 /*
2964  * Call when urgflag != 0 to handle Out Of Band commands.
2965  * Returns non zero if the OOB command aborted the transfer
2966  * by setting transflag to 0. (c.f., "ABOR").
2967  */
2968 static int
handleoobcmd(void)2969 handleoobcmd(void)
2970 {
2971           char *cp;
2972           int ret;
2973 
2974           if (!urgflag)
2975                     return (0);
2976           urgflag = 0;
2977           /* only process if transfer occurring */
2978           if (!transflag)
2979                     return (0);
2980           cp = tmpline;
2981           ret = get_line(cp, sizeof(tmpline)-1, stdin);
2982           if (ret == -1) {
2983                     reply(221, "You could at least say goodbye.");
2984                     dologout(0);
2985           } else if (ret == -2) {
2986                     /* Ignore truncated command */
2987                     /* XXX: abort xfer with "500 command too long", & return 1 ? */
2988                     return 0;
2989           }
2990                     /*
2991                      * Manually parse OOB commands, because we can't
2992                      * recursively call the yacc parser...
2993                      */
2994           if (strcasecmp(cp, "ABOR\r\n") == 0) {
2995                     abor();
2996           } else if (strcasecmp(cp, "STAT\r\n") == 0) {
2997                     statxfer();
2998           } else {
2999                     /* XXX: error with "500 unknown command" ? */
3000           }
3001           return (transflag == 0);
3002 }
3003 
3004 static int
bind_pasv_addr(void)3005 bind_pasv_addr(void)
3006 {
3007           static int passiveport;
3008           int port, len;
3009 
3010           len = pasv_addr.su_len;
3011           if (curclass.portmin == 0 && curclass.portmax == 0) {
3012                     pasv_addr.su_port = 0;
3013                     return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len));
3014           }
3015 
3016           if (passiveport == 0) {
3017                     srand(getpid());
3018                     passiveport = rand() % (curclass.portmax - curclass.portmin)
3019                         + curclass.portmin;
3020           }
3021 
3022           port = passiveport;
3023           while (1) {
3024                     port++;
3025                     if (port > curclass.portmax)
3026                               port = curclass.portmin;
3027                     else if (port == passiveport) {
3028                               errno = EAGAIN;
3029                               return (-1);
3030                     }
3031                     pasv_addr.su_port = htons(port);
3032                     if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0)
3033                               break;
3034                     if (errno != EADDRINUSE)
3035                               return (-1);
3036           }
3037           passiveport = port;
3038           return (0);
3039 }
3040 
3041 /*
3042  * Note: a response of 425 is not mentioned as a possible response to
3043  *        the PASV command in RFC959. However, it has been blessed as
3044  *        a legitimate response by Jon Postel in a telephone conversation
3045  *        with Rick Adams on 25 Jan 89.
3046  */
3047 void
passive(void)3048 passive(void)
3049 {
3050           socklen_t len;
3051           int recvbufsize;
3052           char *p, *a;
3053 
3054           if (pdata >= 0)
3055                     close(pdata);
3056           pdata = socket(AF_INET, SOCK_STREAM, 0);
3057           if (pdata < 0 || !logged_in) {
3058                     perror_reply(425, "Can't open passive connection");
3059                     return;
3060           }
3061           pasv_addr = ctrl_addr;
3062 
3063           if (bind_pasv_addr() < 0)
3064                     goto pasv_error;
3065           len = pasv_addr.su_len;
3066           if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
3067                     goto pasv_error;
3068           pasv_addr.su_len = len;
3069           if (curclass.recvbufsize) {
3070                     recvbufsize = curclass.recvbufsize;
3071                     if (setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, &recvbufsize,
3072                                      sizeof(int)) == -1)
3073                               syslog(LOG_WARNING, "setsockopt(SO_RCVBUF, %d): %m",
3074                                      recvbufsize);
3075           }
3076           if (listen(pdata, 1) < 0)
3077                     goto pasv_error;
3078           if (curclass.advertise.su_len != 0)
3079                     a = (char *) &curclass.advertise.su_addr;
3080           else
3081                     a = (char *) &pasv_addr.su_addr;
3082           p = (char *) &pasv_addr.su_port;
3083 
3084 #define UC(b) (((int) b) & 0xff)
3085 
3086           reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
3087                     UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
3088           return;
3089 
3090  pasv_error:
3091           (void) close(pdata);
3092           pdata = -1;
3093           perror_reply(425, "Can't open passive connection");
3094           return;
3095 }
3096 
3097 /*
3098  * convert protocol identifier to/from AF
3099  */
3100 int
lpsvproto2af(int proto)3101 lpsvproto2af(int proto)
3102 {
3103 
3104           switch (proto) {
3105           case 4:
3106                     return AF_INET;
3107 #ifdef INET6
3108           case 6:
3109                     return AF_INET6;
3110 #endif
3111           default:
3112                     return -1;
3113           }
3114 }
3115 
3116 int
af2lpsvproto(int af)3117 af2lpsvproto(int af)
3118 {
3119 
3120           switch (af) {
3121           case AF_INET:
3122                     return 4;
3123 #ifdef INET6
3124           case AF_INET6:
3125                     return 6;
3126 #endif
3127           default:
3128                     return -1;
3129           }
3130 }
3131 
3132 int
epsvproto2af(int proto)3133 epsvproto2af(int proto)
3134 {
3135 
3136           switch (proto) {
3137           case 1:
3138                     return AF_INET;
3139 #ifdef INET6
3140           case 2:
3141                     return AF_INET6;
3142 #endif
3143           default:
3144                     return -1;
3145           }
3146 }
3147 
3148 int
af2epsvproto(int af)3149 af2epsvproto(int af)
3150 {
3151 
3152           switch (af) {
3153           case AF_INET:
3154                     return 1;
3155 #ifdef INET6
3156           case AF_INET6:
3157                     return 2;
3158 #endif
3159           default:
3160                     return -1;
3161           }
3162 }
3163 
3164 /*
3165  * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3166  * 229 Entering Extended Passive Mode (|||port|)
3167  */
3168 void
long_passive(const char * cmd,int pf)3169 long_passive(const char *cmd, int pf)
3170 {
3171           socklen_t len;
3172           char *p, *a;
3173 
3174           if (!logged_in) {
3175                     syslog(LOG_NOTICE, "long passive but not logged in");
3176                     reply(503, "Login with USER first.");
3177                     return;
3178           }
3179 
3180           if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
3181                     /*
3182                      * XXX: only EPRT/EPSV ready clients will understand this
3183                      */
3184                     if (strcmp(cmd, "EPSV") != 0)
3185                               reply(501, "Network protocol mismatch"); /*XXX*/
3186                     else
3187                               epsv_protounsupp("Network protocol mismatch");
3188 
3189                     return;
3190           }
3191 
3192           if (pdata >= 0)
3193                     close(pdata);
3194           pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
3195           if (pdata < 0) {
3196                     perror_reply(425, "Can't open passive connection");
3197                     return;
3198           }
3199           pasv_addr = ctrl_addr;
3200           if (bind_pasv_addr() < 0)
3201                     goto pasv_error;
3202           len = pasv_addr.su_len;
3203           if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
3204                     goto pasv_error;
3205           pasv_addr.su_len = len;
3206           if (listen(pdata, 1) < 0)
3207                     goto pasv_error;
3208           p = (char *) &pasv_addr.su_port;
3209 
3210 #define UC(b) (((int) b) & 0xff)
3211 
3212           if (strcmp(cmd, "LPSV") == 0) {
3213                     struct sockinet *advert;
3214 
3215                     if (curclass.advertise.su_len != 0)
3216                               advert = &curclass.advertise;
3217                     else
3218                               advert = &pasv_addr;
3219                     switch (advert->su_family) {
3220                     case AF_INET:
3221                               a = (char *) &advert->su_addr;
3222                               reply(228,
3223     "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3224                                         4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3225                                         2, UC(p[0]), UC(p[1]));
3226                               return;
3227 #ifdef INET6
3228                     case AF_INET6:
3229                               a = (char *) &advert->su_6addr;
3230                               reply(228,
3231     "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)",
3232                                         6, 16,
3233                                         UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3234                                         UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
3235                                         UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
3236                                         UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
3237                                         2, UC(p[0]), UC(p[1]));
3238                               return;
3239 #endif
3240                     }
3241 #undef UC
3242           } else if (strcmp(cmd, "EPSV") == 0) {
3243                     switch (pasv_addr.su_family) {
3244                     case AF_INET:
3245 #ifdef INET6
3246                     case AF_INET6:
3247 #endif
3248                               reply(229, "Entering Extended Passive Mode (|||%d|)",
3249                                   ntohs(pasv_addr.su_port));
3250                               return;
3251                     }
3252           } else {
3253                     /* more proper error code? */
3254           }
3255 
3256  pasv_error:
3257           (void) close(pdata);
3258           pdata = -1;
3259           perror_reply(425, "Can't open passive connection");
3260           return;
3261 }
3262 
3263 int
extended_port(const char * arg)3264 extended_port(const char *arg)
3265 {
3266           char *tmp = NULL;
3267           char *result[3];
3268           char *p, *q;
3269           char delim;
3270           struct addrinfo hints;
3271           struct addrinfo *res = NULL;
3272           int i;
3273           unsigned long proto;
3274 
3275           tmp = ftpd_strdup(arg);
3276           p = tmp;
3277           delim = p[0];
3278           p++;
3279           memset(result, 0, sizeof(result));
3280           for (i = 0; i < 3; i++) {
3281                     q = strchr(p, delim);
3282                     if (!q || *q != delim)
3283                               goto parsefail;
3284                     *q++ = '\0';
3285                     result[i] = p;
3286                     p = q;
3287           }
3288 
3289                               /* some more sanity checks */
3290           errno = 0;
3291           p = NULL;
3292           (void)strtoul(result[2], &p, 10);
3293           if (errno || !*result[2] || *p)
3294                     goto parsefail;
3295           errno = 0;
3296           p = NULL;
3297           proto = strtoul(result[0], &p, 10);
3298           if (errno || !*result[0] || *p)
3299                     goto protounsupp;
3300 
3301           memset(&hints, 0, sizeof(hints));
3302           hints.ai_family = epsvproto2af((int)proto);
3303           if (hints.ai_family < 0)
3304                     goto protounsupp;
3305           hints.ai_socktype = SOCK_STREAM;
3306           hints.ai_flags = AI_NUMERICHOST;
3307           if (getaddrinfo(result[1], result[2], &hints, &res))
3308                     goto parsefail;
3309           if (res->ai_next)
3310                     goto parsefail;
3311           if (sizeof(data_dest) < res->ai_addrlen)
3312                     goto parsefail;
3313           memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen);
3314           data_dest.su_len = res->ai_addrlen;
3315 #ifdef INET6
3316           if (his_addr.su_family == AF_INET6 &&
3317               data_dest.su_family == AF_INET6) {
3318                               /* XXX: more sanity checks! */
3319                     data_dest.su_scope_id = his_addr.su_scope_id;
3320           }
3321 #endif
3322 
3323           if (tmp != NULL)
3324                     free(tmp);
3325           if (res)
3326                     freeaddrinfo(res);
3327           return 0;
3328 
3329  parsefail:
3330           reply(500, "Invalid argument, rejected.");
3331           usedefault = 1;
3332           if (tmp != NULL)
3333                     free(tmp);
3334           if (res)
3335                     freeaddrinfo(res);
3336           return -1;
3337 
3338  protounsupp:
3339           epsv_protounsupp("Protocol not supported");
3340           usedefault = 1;
3341           if (tmp != NULL)
3342                     free(tmp);
3343           return -1;
3344 }
3345 
3346 /*
3347  * 522 Protocol not supported (proto,...)
3348  * as we assume address family for control and data connections are the same,
3349  * we do not return the list of address families we support - instead, we
3350  * return the address family of the control connection.
3351  */
3352 void
epsv_protounsupp(const char * message)3353 epsv_protounsupp(const char *message)
3354 {
3355           int proto;
3356 
3357           proto = af2epsvproto(ctrl_addr.su_family);
3358           if (proto < 0)
3359                     reply(501, "%s", message);    /* XXX */
3360           else
3361                     reply(522, "%s, use (%d)", message, proto);
3362 }
3363 
3364 /*
3365  * Generate unique name for file with basename "local".
3366  * The file named "local" is already known to exist.
3367  * Generates failure reply on error.
3368  *
3369  * XXX:   this function should under go changes similar to
3370  *        the mktemp(3)/mkstemp(3) changes.
3371  */
3372 static char *
gunique(const char * local)3373 gunique(const char *local)
3374 {
3375           static char new[MAXPATHLEN];
3376           struct stat st;
3377           char *cp;
3378           int count;
3379 
3380           cp = strrchr(local, '/');
3381           if (cp)
3382                     *cp = '\0';
3383           if (stat(cp ? local : ".", &st) < 0) {
3384                     perror_reply(553, cp ? local : ".");
3385                     return (NULL);
3386           }
3387           if (cp)
3388                     *cp = '/';
3389           for (count = 1; count < 100; count++) {
3390                     (void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count);
3391                     if (stat(new, &st) < 0)
3392                               return (new);
3393           }
3394           reply(452, "Unique file name cannot be created.");
3395           return (NULL);
3396 }
3397 
3398 /*
3399  * Format and send reply containing system error number.
3400  */
3401 void
perror_reply(int code,const char * string)3402 perror_reply(int code, const char *string)
3403 {
3404           int save_errno;
3405 
3406           save_errno = errno;
3407           reply(code, "%s: %s.", string, strerror(errno));
3408           errno = save_errno;
3409 }
3410 
3411 static char *onefile[] = {
3412           NULL,
3413           0
3414 };
3415 
3416 void
send_file_list(const char * whichf)3417 send_file_list(const char *whichf)
3418 {
3419           struct stat st;
3420           DIR *dirp;
3421           struct dirent *dir;
3422           FILE *volatile dout;
3423           char **volatile dirlist;
3424           char *dirname, *p;
3425           char *notglob;
3426           int volatile simple;
3427           int volatile freeglob;
3428           glob_t gl;
3429 
3430           dirp = NULL;
3431           dout = NULL;
3432           notglob = NULL;
3433           simple = 0;
3434           freeglob = 0;
3435           urgflag = 0;
3436 
3437           p = NULL;
3438           if (strpbrk(whichf, "~{[*?") != NULL) {
3439                     int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
3440 
3441                     memset(&gl, 0, sizeof(gl));
3442                     freeglob = 1;
3443                     if (glob(whichf, flags, 0, &gl)) {
3444                               reply(450, "Not found");
3445                               goto cleanup_send_file_list;
3446                     } else if (gl.gl_pathc == 0) {
3447                               errno = ENOENT;
3448                               perror_reply(450, whichf);
3449                               goto cleanup_send_file_list;
3450                     }
3451                     dirlist = gl.gl_pathv;
3452           } else {
3453                     notglob = ftpd_strdup(whichf);
3454                     onefile[0] = notglob;
3455                     dirlist = onefile;
3456                     simple = 1;
3457           }
3458                                                   /* XXX: } for vi sm */
3459 
3460           while ((dirname = *dirlist++) != NULL) {
3461                     int trailingslash = 0;
3462 
3463                     if (stat(dirname, &st) < 0) {
3464                               /*
3465                                * If user typed "ls -l", etc, and the client
3466                                * used NLST, do what the user meant.
3467                                */
3468                               /* XXX: nuke this support? */
3469                               if (dirname[0] == '-' && *dirlist == NULL &&
3470                                   transflag == 0) {
3471                                         const char *argv[] = { INTERNAL_LS, "", NULL };
3472 
3473                                         argv[1] = dirname;
3474                                         retrieve(argv, dirname);
3475                                         goto cleanup_send_file_list;
3476                               }
3477                               perror_reply(450, whichf);
3478                               goto cleanup_send_file_list;
3479                     }
3480 
3481                     if (S_ISREG(st.st_mode)) {
3482                               /*
3483                                * XXXRFC:
3484                                *        should we follow RFC959 and not work
3485                                *        for non directories?
3486                                */
3487                               if (dout == NULL) {
3488                                         dout = dataconn("file list", (off_t)-1, "w");
3489                                         if (dout == NULL)
3490                                                   goto cleanup_send_file_list;
3491                                         transflag = 1;
3492                               }
3493                               cprintf(dout, "%s%s\n", dirname,
3494                                   type == TYPE_A ? "\r" : "");
3495                               continue;
3496                     } else if (!S_ISDIR(st.st_mode))
3497                               continue;
3498 
3499                     if (dirname[strlen(dirname) - 1] == '/')
3500                               trailingslash++;
3501 
3502                     if ((dirp = opendir(dirname)) == NULL)
3503                               continue;
3504 
3505                     while ((dir = readdir(dirp)) != NULL) {
3506                               char nbuf[MAXPATHLEN];
3507 
3508                               if (urgflag && handleoobcmd()) {
3509                                         (void) closedir(dirp);
3510                                         goto cleanup_send_file_list;
3511                               }
3512 
3513                               if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
3514                                         continue;
3515 
3516                               (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
3517                                   trailingslash ? "" : "/", dir->d_name);
3518 
3519                               /*
3520                                * We have to do a stat to ensure it's
3521                                * not a directory or special file.
3522                                */
3523                               /*
3524                                * XXXRFC:
3525                                *        should we follow RFC959 and filter out
3526                                *        non files ?   lukem - NO!, or not until
3527                                *        our ftp client uses MLS{T,D} for completion.
3528                                */
3529                               if (simple || (stat(nbuf, &st) == 0 &&
3530                                   S_ISREG(st.st_mode))) {
3531                                         if (dout == NULL) {
3532                                                   dout = dataconn("file list", (off_t)-1,
3533                                                             "w");
3534                                                   if (dout == NULL) {
3535                                                             (void) closedir(dirp);
3536                                                             goto cleanup_send_file_list;
3537                                                   }
3538                                                   transflag = 1;
3539                                         }
3540                                         p = nbuf;
3541                                         if (nbuf[0] == '.' && nbuf[1] == '/')
3542                                                   p = &nbuf[2];
3543                                         cprintf(dout, "%s%s\n", p,
3544                                             type == TYPE_A ? "\r" : "");
3545                               }
3546                     }
3547                     (void) closedir(dirp);
3548           }
3549 
3550           if (dout == NULL)
3551                     reply(450, "No files found.");
3552           else if (ferror(dout) != 0)
3553                     perror_reply(451, "Data connection");
3554           else
3555                     reply(226, "Transfer complete.");
3556 
3557  cleanup_send_file_list:
3558           closedataconn(dout);
3559           transflag = 0;
3560           urgflag = 0;
3561           total_xfers++;
3562           total_xfers_out++;
3563           if (notglob)
3564                     free(notglob);
3565           if (freeglob)
3566                     globfree(&gl);
3567 }
3568 
3569 char *
conffilename(const char * s)3570 conffilename(const char *s)
3571 {
3572           static char filename[MAXPATHLEN];
3573 
3574           if (*s == '/')
3575                     strlcpy(filename, s, sizeof(filename));
3576           else
3577                     (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
3578           return (filename);
3579 }
3580 
3581 /*
3582  * logxfer --
3583  *        if logging > 1, then based on the arguments, syslog a message:
3584  *         if bytes != -1               "<command> <file1> = <bytes> bytes"
3585  *         else if file2 != NULL        "<command> <file1> <file2>"
3586  *         else                         "<command> <file1>"
3587  *        if elapsed != NULL, append "in xxx.yyy seconds"
3588  *        if error != NULL, append ": " + error
3589  *
3590  *        if doxferlog != 0, bytes != -1, and command is "get", "put",
3591  *        or "append", syslog and/or write a wu-ftpd style xferlog entry
3592  */
3593 void
logxfer(const char * command,off_t bytes,const char * file1,const char * file2,const struct timeval * elapsed,const char * error)3594 logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
3595     const struct timeval *elapsed, const char *error)
3596 {
3597           char                 buf[MAXPATHLEN * 2 + 100];
3598           char                 realfile1[MAXPATHLEN], realfile2[MAXPATHLEN];
3599           const char          *r1, *r2;
3600           char                 direction;
3601           size_t               len;
3602           time_t               now;
3603 
3604           if (logging <=1 && !doxferlog)
3605                     return;
3606 
3607           r1 = r2 = NULL;
3608           if ((r1 = realpath(file1, realfile1)) == NULL)
3609                     r1 = file1;
3610           if (file2 != NULL)
3611                     if ((r2 = realpath(file2, realfile2)) == NULL)
3612                               r2 = file2;
3613 
3614                     /*
3615                      * syslog command
3616                      */
3617           if (logging > 1) {
3618                     len = snprintf(buf, sizeof(buf), "%s %s", command, r1);
3619                     if (bytes != (off_t)-1)
3620                               len += snprintf(buf + len, sizeof(buf) - len,
3621                                   " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes));
3622                     else if (r2 != NULL)
3623                               len += snprintf(buf + len, sizeof(buf) - len,
3624                                   " %s", r2);
3625                     if (elapsed != NULL)
3626                               len += snprintf(buf + len, sizeof(buf) - len,
3627                                   " in " LLF ".%.03ld seconds",
3628                                   (LLT)elapsed->tv_sec,
3629                                   (long)(elapsed->tv_usec / 1000));
3630                     if (error != NULL)
3631                               len += snprintf(buf + len, sizeof(buf) - len,
3632                                   ": %s", error);
3633                     syslog(LOG_INFO, "%s", buf);
3634           }
3635 
3636                     /*
3637                      * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3638                      */
3639           if (!doxferlog || bytes == -1)
3640                     return;
3641 
3642           if (strcmp(command, "get") == 0)
3643                     direction = 'o';
3644           else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0)
3645                     direction = 'i';
3646           else
3647                     return;
3648 
3649           time(&now);
3650           len = snprintf(buf, sizeof(buf),
3651               "%.24s " LLF " %s " LLF " %s %c %s %c %c %s FTP 0 * %c\n",
3652 
3653 /*
3654  * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
3655  *        the full date.  This may be problematic for accurate log parsing,
3656  *        given that syslog messages don't contain the full date.
3657  */
3658               ctime(&now),
3659               (LLT)
3660               (elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0)),
3661               remotehost,
3662               (LLT) bytes,
3663               r1,
3664               type == TYPE_A ? 'a' : 'b',
3665               "_",            /* XXX: take conversions into account? */
3666               direction,
3667 
3668               curclass.type == CLASS_GUEST ?  'a' :
3669               curclass.type == CLASS_CHROOT ? 'g' :
3670               curclass.type == CLASS_REAL ?   'r' : '?',
3671 
3672               curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
3673               error != NULL ? 'i' : 'c'
3674               );
3675 
3676           if ((doxferlog & 2) && xferlogfd != -1)
3677                     write(xferlogfd, buf, len);
3678           if ((doxferlog & 1)) {
3679                     buf[len-1] = '\n';  /* strip \n from syslog message */
3680                     syslog(LOG_INFO, "xferlog: %s", buf);
3681           }
3682 }
3683 
3684 /*
3685  * Log the resource usage.
3686  *
3687  * XXX: more resource usage to logging?
3688  */
3689 void
logrusage(const struct rusage * rusage_before,const struct rusage * rusage_after)3690 logrusage(const struct rusage *rusage_before,
3691     const struct rusage *rusage_after)
3692 {
3693           struct timeval usrtime, systime;
3694 
3695           if (logging <= 1)
3696                     return;
3697 
3698           timersub(&rusage_after->ru_utime, &rusage_before->ru_utime, &usrtime);
3699           timersub(&rusage_after->ru_stime, &rusage_before->ru_stime, &systime);
3700           syslog(LOG_INFO, LLF ".%.03ldu " LLF ".%.03lds %ld+%ldio %ldpf+%ldw",
3701               (LLT)usrtime.tv_sec, (long)(usrtime.tv_usec / 1000),
3702               (LLT)systime.tv_sec, (long)(systime.tv_usec / 1000),
3703               rusage_after->ru_inblock - rusage_before->ru_inblock,
3704               rusage_after->ru_oublock - rusage_before->ru_oublock,
3705               rusage_after->ru_majflt - rusage_before->ru_majflt,
3706               rusage_after->ru_nswap - rusage_before->ru_nswap);
3707 }
3708 
3709 /*
3710  * Determine if `password' is valid for user given in `pw'.
3711  * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
3712  */
3713 int
checkpassword(const struct passwd * pwent,const char * password)3714 checkpassword(const struct passwd *pwent, const char *password)
3715 {
3716           const char *orig;
3717           char      *new;
3718           time_t     change, expire, now;
3719 
3720           change = expire = 0;
3721           if (pwent == NULL)
3722                     return 1;
3723 
3724           time(&now);
3725           orig = pwent->pw_passwd;      /* save existing password */
3726           expire = pwent->pw_expire;
3727           change = pwent->pw_change;
3728           if (change == _PASSWORD_CHGNOW)
3729                     change = now;
3730 
3731           if (orig[0] == '\0')                    /* don't allow empty passwords */
3732                     return 1;
3733 
3734           new = crypt(password, orig);  /* encrypt given password */
3735           if (strcmp(new, orig) != 0)   /* compare */
3736                     return 1;
3737 
3738           if ((expire && now >= expire) || (change && now >= change))
3739                     return 2;           /* check if expired */
3740 
3741           return 0;                     /* OK! */
3742 }
3743 
3744 char *
ftpd_strdup(const char * s)3745 ftpd_strdup(const char *s)
3746 {
3747           char *new = strdup(s);
3748 
3749           if (new == NULL)
3750                     fatal("Local resource failure: malloc");
3751                     /* NOTREACHED */
3752           return (new);
3753 }
3754 
3755 /*
3756  * As per fprintf(), but increment total_bytes and total_bytes_out,
3757  * by the appropriate amount.
3758  */
3759 void
cprintf(FILE * fd,const char * fmt,...)3760 cprintf(FILE *fd, const char *fmt, ...)
3761 {
3762           off_t b;
3763           va_list ap;
3764 
3765           va_start(ap, fmt);
3766           b = vfprintf(fd, fmt, ap);
3767           va_end(ap);
3768           total_bytes += b;
3769           total_bytes_out += b;
3770 }
3771 
3772 #ifdef USE_PAM
3773 /*
3774  * the following code is stolen from imap-uw PAM authentication module and
3775  * login.c
3776  */
3777 typedef struct {
3778           const char *uname;  /* user name */
3779           int           triedonce;      /* if non-zero, tried before */
3780 } ftpd_cred_t;
3781 
3782 static int
auth_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata)3783 auth_conv(int num_msg, const struct pam_message **msg,
3784     struct pam_response **resp, void *appdata)
3785 {
3786           int i, ret;
3787           size_t n;
3788           ftpd_cred_t *cred = (ftpd_cred_t *) appdata;
3789           struct pam_response *myreply;
3790           char pbuf[FTP_BUFLEN];
3791 
3792           if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
3793                     return (PAM_CONV_ERR);
3794           myreply = calloc(num_msg, sizeof *myreply);
3795           if (myreply == NULL)
3796                     return PAM_BUF_ERR;
3797 
3798           for (i = 0; i < num_msg; i++) {
3799                     myreply[i].resp_retcode = 0;
3800                     myreply[i].resp = NULL;
3801                     switch (msg[i]->msg_style) {
3802                     case PAM_PROMPT_ECHO_ON:      /* user */
3803                               myreply[i].resp = ftpd_strdup(cred->uname);
3804                               /* PAM frees resp. */
3805                               break;
3806                     case PAM_PROMPT_ECHO_OFF:     /* authtok (password) */
3807                                         /*
3808                                          * Only send a single 331 reply and
3809                                          * then expect a PASS.
3810                                          */
3811                               if (cred->triedonce) {
3812                                         syslog(LOG_ERR,
3813                               "auth_conv: already performed PAM_PROMPT_ECHO_OFF");
3814                                         goto fail;
3815                               }
3816                               cred->triedonce++;
3817                               if (msg[i]->msg[0] == '\0') {
3818                                         (void)strlcpy(pbuf, "password", sizeof(pbuf));
3819                               } else {
3820                                                   /* Uncapitalize msg */
3821                                         (void)strlcpy(pbuf, msg[i]->msg, sizeof(pbuf));
3822                                         if (isupper((unsigned char)pbuf[0]))
3823                                                   pbuf[0] = tolower(
3824                                                       (unsigned char)pbuf[0]);
3825                                                   /* Remove trailing ':' and whitespace */
3826                                         n = strlen(pbuf);
3827                                         while (n-- > 0) {
3828                                                   if (isspace((unsigned char)pbuf[n]) ||
3829                                                       pbuf[n] == ':')
3830                                                             pbuf[n] = '\0';
3831                                                   else
3832                                                             break;
3833                                         }
3834                               }
3835                                         /* Send reply, wait for a response. */
3836                               reply(331, "User %s accepted, provide %s.",
3837                                   cred->uname, pbuf);
3838                               (void) alarm(curclass.timeout);
3839                               ret = get_line(pbuf, sizeof(pbuf)-1, stdin);
3840                               (void) alarm(0);
3841                               if (ret == -1) {
3842                                         reply(221, "You could at least say goodbye.");
3843                                         dologout(0);
3844                               } else if (ret == -2) {
3845                                   /* XXX: should we do this reply(-530, ..) ? */
3846                                         reply(-530, "Command too long.");
3847                                         goto fail;
3848                               }
3849                                         /* Ensure it is PASS */
3850                               if (strncasecmp(pbuf, "PASS ", 5) != 0) {
3851                                         syslog(LOG_ERR,
3852                                             "auth_conv: unexpected reply '%.4s'", pbuf);
3853                                         /* XXX: should we do this reply(-530, ..) ? */
3854                                         reply(-530, "Unexpected reply '%.4s'.", pbuf);
3855                                         goto fail;
3856                               }
3857                                         /* Strip CRLF from "PASS" reply */
3858                               n = strlen(pbuf);
3859                               while (--n >= 5 &&
3860                                   (pbuf[n] == '\r' || pbuf[n] == '\n'))
3861                                   pbuf[n] = '\0';
3862                                         /* Copy password into reply */
3863                               myreply[i].resp = ftpd_strdup(pbuf+5);
3864                                         /* PAM frees resp. */
3865                               break;
3866                     case PAM_TEXT_INFO:
3867                     case PAM_ERROR_MSG:
3868                               break;
3869                     default:                      /* unknown message style */
3870                               goto fail;
3871                     }
3872           }
3873 
3874           *resp = myreply;
3875           return PAM_SUCCESS;
3876 
3877  fail:
3878           free(myreply);
3879           *resp = NULL;
3880           return PAM_CONV_ERR;
3881 }
3882 
3883 /*
3884  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
3885  * authenticated, or 1 if not authenticated.  If some sort of PAM system
3886  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3887  * function returns -1.  This can be used as an indication that we should
3888  * fall back to a different authentication mechanism.
3889  * pw maybe be updated to a new user if PAM_USER changes from curname.
3890  */
3891 static int
auth_pam(void)3892 auth_pam(void)
3893 {
3894           const char *tmpl_user;
3895           const void *item;
3896           int rval;
3897           int e;
3898           ftpd_cred_t auth_cred = { curname, 0 };
3899           struct pam_conv conv = { &auth_conv, &auth_cred };
3900           struct sockaddr_storage ss;
3901 
3902           e = pam_start("ftpd", curname, &conv, &pamh);
3903           if (e != PAM_SUCCESS) {
3904                     /*
3905                      * In OpenPAM, it's OK to pass NULL to pam_strerror()
3906                      * if context creation has failed in the first place.
3907                      */
3908                     syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e));
3909                     return -1;
3910           }
3911 
3912           e = pam_set_item(pamh, PAM_RHOST, remotehost);
3913           if (e != PAM_SUCCESS) {
3914                     syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
3915                               pam_strerror(pamh, e));
3916                     if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3917                               syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3918                     }
3919                     pamh = NULL;
3920                     return -1;
3921           }
3922 
3923           memset(&ss, 0, sizeof(ss));
3924           memcpy(&ss, &his_addr.si_su, his_addr.su_len);
3925           e = pam_set_item(pamh, PAM_SOCKADDR, &ss);
3926           if (e != PAM_SUCCESS) {
3927                     syslog(LOG_ERR, "pam_set_item(PAM_SOCKADDR): %s",
3928                               pam_strerror(pamh, e));
3929                     if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3930                               syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3931                     }
3932                     pamh = NULL;
3933                     return -1;
3934           }
3935 
3936           e = pam_authenticate(pamh, 0);
3937           if (ftpd_debug)
3938                     syslog(LOG_DEBUG, "pam_authenticate: user '%s' returned %d",
3939                         curname, e);
3940           switch (e) {
3941           case PAM_SUCCESS:
3942                     /*
3943                      * With PAM we support the concept of a "template"
3944                      * user.  The user enters a login name which is
3945                      * authenticated by PAM, usually via a remote service
3946                      * such as RADIUS or TACACS+.  If authentication
3947                      * succeeds, a different but related "template" name
3948                      * is used for setting the credentials, shell, and
3949                      * home directory.  The name the user enters need only
3950                      * exist on the remote authentication server, but the
3951                      * template name must be present in the local password
3952                      * database.
3953                      *
3954                      * This is supported by two various mechanisms in the
3955                      * individual modules.  However, from the application's
3956                      * point of view, the template user is always passed
3957                      * back as a changed value of the PAM_USER item.
3958                      */
3959                     if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
3960                         PAM_SUCCESS) {
3961                               tmpl_user = (const char *) item;
3962                               if (pw == NULL
3963                                   || strcmp(pw->pw_name, tmpl_user) != 0) {
3964                                         pw = sgetpwnam(tmpl_user);
3965                                         if (ftpd_debug)
3966                                                   syslog(LOG_DEBUG,
3967                                                       "auth_pam: PAM changed "
3968                                                       "user from '%s' to '%s'",
3969                                                       curname, pw->pw_name);
3970                                         (void)strlcpy(curname, pw->pw_name,
3971                                             curname_len);
3972                               }
3973                     } else
3974                               syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
3975                                   pam_strerror(pamh, e));
3976                     rval = 0;
3977                     break;
3978 
3979           case PAM_AUTH_ERR:
3980           case PAM_USER_UNKNOWN:
3981           case PAM_MAXTRIES:
3982                     rval = 1;
3983                     break;
3984 
3985           default:
3986                     syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
3987                     rval = -1;
3988                     break;
3989           }
3990 
3991           if (rval == 0) {
3992                     e = pam_acct_mgmt(pamh, 0);
3993                     if (e != PAM_SUCCESS) {
3994                               syslog(LOG_ERR, "pam_acct_mgmt: %s",
3995                                                             pam_strerror(pamh, e));
3996                               rval = 1;
3997                     }
3998           }
3999 
4000           if (rval != 0) {
4001                     if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
4002                               syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
4003                     }
4004                     pamh = NULL;
4005           }
4006           return rval;
4007 }
4008 
4009 #endif /* USE_PAM */
4010