[BACK]Return to ftpd.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / ftpd

Annotation of src/libexec/ftpd/ftpd.c, Revision 1.128

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

CVSweb <webmaster@jp.NetBSD.org>