[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.122

1.122   ! christos    1: /*     $NetBSD: ftpd.c,v 1.121 2001/02/04 22:04:12 christos Exp $      */
1.67      itojun      2:
                      3: /*
1.82      lukem       4:  * Copyright (c) 1997-2000 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.122   ! christos  112: __RCSID("$NetBSD: ftpd.c,v 1.121 2001/02/04 22:04:12 christos 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.111     lukem     262:        version = FTPD_VERSION;
                    263:
                    264:        /*
                    265:         * LOG_NDELAY sets up the logging connection immediately,
                    266:         * necessary for anonymous ftp's that chroot and can't do it later.
                    267:         */
                    268:        openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
1.1       cgd       269:
1.118     lukem     270:        while ((ch = getopt(argc, argv, "a:c:C:de:h:HlP:qQrst:T:uUvV:wWX"))
1.111     lukem     271:            != -1) {
1.8       deraadt   272:                switch (ch) {
1.22      cjs       273:                case 'a':
                    274:                        anondir = optarg;
                    275:                        break;
                    276:
1.34      lukem     277:                case 'c':
1.111     lukem     278:                        confdir = optarg;
1.34      lukem     279:                        break;
1.73      lukem     280:
1.35      lukem     281:                case 'C':
1.81      lukem     282:                        pw = sgetpwnam(optarg);
1.73      lukem     283:                        exit(checkaccess(optarg) ? 0 : 1);
1.35      lukem     284:                        /* NOTREACHED */
1.34      lukem     285:
1.1       cgd       286:                case 'd':
1.22      cjs       287:                case 'v':               /* deprecated */
1.1       cgd       288:                        debug = 1;
                    289:                        break;
                    290:
1.111     lukem     291:                case 'e':
                    292:                        emailaddr = optarg;
                    293:                        break;
                    294:
1.80      lukem     295:                case 'h':
                    296:                        strlcpy(hostname, optarg, sizeof(hostname));
1.99      lukem     297:                        break;
                    298:
                    299:                case 'H':
                    300:                        if (gethostname(hostname, sizeof(hostname)) == -1)
                    301:                                hostname[0] = '\0';
                    302:                        hostname[sizeof(hostname) - 1] = '\0';
1.80      lukem     303:                        break;
                    304:
1.1       cgd       305:                case 'l':
1.8       deraadt   306:                        logging++;      /* > 1 == extra logging */
1.1       cgd       307:                        break;
                    308:
1.111     lukem     309:                case 'P':
                    310:                        dataport = (int)strtol(optarg, &p, 10);
                    311:                        if (*p != '\0' || dataport < IPPORT_RESERVED ||
                    312:                            dataport > IPPORT_ANONMAX) {
                    313:                                syslog(LOG_WARNING, "Invalid dataport %s",
                    314:                                    optarg);
                    315:                                dataport = 0;
                    316:                        }
                    317:                        break;
                    318:
                    319:                case 'q':
                    320:                        dopidfile = 1;
                    321:                        break;
                    322:
                    323:                case 'Q':
                    324:                        dopidfile = 0;
                    325:                        break;
                    326:
1.101     lukem     327:                case 'r':
                    328:                        dropprivs = 1;
                    329:                        break;
                    330:
1.51      msaitoh   331:                case 's':
                    332:                        sflag = 1;
                    333:                        break;
                    334:
1.1       cgd       335:                case 't':
                    336:                case 'T':
1.119     lukem     337:                        syslog(LOG_WARNING,
1.111     lukem     338:                            "-%c has been deprecated in favour of ftpd.conf",
                    339:                            ch);
                    340:                        break;
                    341:
1.1       cgd       342:                case 'u':
1.111     lukem     343:                        doutmp = 1;
1.8       deraadt   344:                        break;
1.1       cgd       345:
1.78      lukem     346:                case 'U':
1.111     lukem     347:                        doutmp = 0;
1.78      lukem     348:                        break;
                    349:
1.101     lukem     350:                case 'V':
                    351:                        if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
                    352:                                version = NULL;
                    353:                        else
                    354:                                version = xstrdup(optarg);
                    355:                        break;
                    356:
1.111     lukem     357:                case 'w':
                    358:                        dowtmp = 1;
                    359:                        break;
                    360:
1.102     lukem     361:                case 'W':
                    362:                        dowtmp = 0;
                    363:                        break;
                    364:
1.118     lukem     365:                case 'X':
                    366:                        doxferlog = 1;
                    367:                        break;
                    368:
1.1       cgd       369:                default:
1.35      lukem     370:                        if (optopt == 'a' || optopt == 'C')
                    371:                                exit(1);
1.119     lukem     372:                        syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
1.1       cgd       373:                        break;
                    374:                }
                    375:        }
1.111     lukem     376:        if (EMPTYSTR(confdir))
                    377:                confdir = _DEFAULT_CONFDIR;
1.35      lukem     378:
1.118     lukem     379:        memset((char *)&his_addr, 0, sizeof(his_addr));
1.109     lukem     380:        addrlen = sizeof(his_addr.si_su);
                    381:        if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
1.35      lukem     382:                syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
                    383:                exit(1);
                    384:        }
1.109     lukem     385:        his_addr.su_len = addrlen;
1.118     lukem     386:        memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr));
1.109     lukem     387:        addrlen = sizeof(ctrl_addr.si_su);
1.68      itojun    388:        if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
                    389:                syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
                    390:                exit(1);
                    391:        }
1.109     lukem     392:        ctrl_addr.su_len = addrlen;
1.104     christos  393: #ifdef INET6
1.67      itojun    394:        if (his_addr.su_family == AF_INET6
1.109     lukem     395:         && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) {
1.68      itojun    396: #if 1
                    397:                /*
                    398:                 * IPv4 control connection arrived to AF_INET6 socket.
                    399:                 * I hate to do this, but this is the easiest solution.
1.90      itojun    400:                 *
                    401:                 * The assumption is untrue on SIIT environment.
1.68      itojun    402:                 */
1.109     lukem     403:                struct sockinet tmp_addr;
1.74      itojun    404:                const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
1.68      itojun    405:
                    406:                tmp_addr = his_addr;
                    407:                memset(&his_addr, 0, sizeof(his_addr));
1.109     lukem     408:                his_addr.su_family = AF_INET;
                    409:                his_addr.su_len = sizeof(his_addr.si_su.su_sin);
                    410:                memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
                    411:                    sizeof(his_addr.su_addr));
                    412:                his_addr.su_port = tmp_addr.su_port;
1.68      itojun    413:
                    414:                tmp_addr = ctrl_addr;
                    415:                memset(&ctrl_addr, 0, sizeof(ctrl_addr));
1.109     lukem     416:                ctrl_addr.su_family = AF_INET;
                    417:                ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin);
                    418:                memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
                    419:                    sizeof(ctrl_addr.su_addr));
                    420:                ctrl_addr.su_port = tmp_addr.su_port;
1.68      itojun    421: #else
1.67      itojun    422:                while (fgets(line, sizeof(line), fd) != NULL) {
                    423:                        if ((cp = strchr(line, '\n')) != NULL)
                    424:                                *cp = '\0';
1.95      lukem     425:                        reply(-530, "%s", line);
1.67      itojun    426:                }
                    427:                (void) fflush(stdout);
                    428:                (void) fclose(fd);
                    429:                reply(530,
1.101     lukem     430:                    "Connection from IPv4 mapped address is not supported.");
1.67      itojun    431:                exit(0);
1.68      itojun    432: #endif
1.75      itojun    433:
                    434:                mapped = 1;
                    435:        } else
1.109     lukem     436: #endif /* INET6 */
1.75      itojun    437:                mapped = 0;
1.35      lukem     438: #ifdef IP_TOS
1.75      itojun    439:        if (!mapped && his_addr.su_family == AF_INET) {
1.67      itojun    440:                tos = IPTOS_LOWDELAY;
                    441:                if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
                    442:                               sizeof(int)) < 0)
                    443:                        syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
                    444:        }
1.35      lukem     445: #endif
1.80      lukem     446:        /* if the hostname hasn't been given, attempt to determine it */
                    447:        if (hostname[0] == '\0') {
1.109     lukem     448:                if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su,
                    449:                    ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0, 0)
                    450:                    != 0)
1.80      lukem     451:                        (void)gethostname(hostname, sizeof(hostname));
                    452:                hostname[sizeof(hostname) - 1] = '\0';
                    453:        }
1.79      lukem     454:
1.35      lukem     455:        /* set this here so klogin can use it... */
                    456:        (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
                    457:
1.1       cgd       458:        (void) freopen(_PATH_DEVNULL, "w", stderr);
                    459:        (void) signal(SIGPIPE, lostconn);
                    460:        (void) signal(SIGCHLD, SIG_IGN);
1.78      lukem     461:        if (signal(SIGURG, myoob) == SIG_ERR)
1.119     lukem     462:                syslog(LOG_WARNING, "signal: %m");
1.1       cgd       463:
                    464:        /* Try to handle urgent data inline */
                    465: #ifdef SO_OOBINLINE
                    466:        if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
1.119     lukem     467:                syslog(LOG_WARNING, "setsockopt: %m");
1.1       cgd       468: #endif
1.66      briggs    469:        /* Set keepalives on the socket to detect dropped connections.  */
                    470: #ifdef SO_KEEPALIVE
                    471:        keepalive = 1;
                    472:        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive,
                    473:            sizeof(int)) < 0)
                    474:                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
                    475: #endif
1.1       cgd       476:
                    477: #ifdef F_SETOWN
                    478:        if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1.119     lukem     479:                syslog(LOG_WARNING, "fcntl F_SETOWN: %m");
1.1       cgd       480: #endif
1.118     lukem     481:        logremotehost(&his_addr);
1.1       cgd       482:        /*
                    483:         * Set up default state
                    484:         */
                    485:        data = -1;
                    486:        type = TYPE_A;
                    487:        form = FORM_N;
                    488:        stru = STRU_F;
                    489:        mode = MODE_S;
                    490:        tmpline[0] = '\0';
1.57      lukem     491:        hasyyerrored = 0;
1.8       deraadt   492:
1.38      mycroft   493: #ifdef KERBEROS5
                    494:        kerror = krb5_init_context(&kcontext);
                    495:        if (kerror) {
1.119     lukem     496:                syslog(LOG_ERR, "%s when initializing Kerberos context",
1.38      mycroft   497:                    error_message(kerror));
                    498:                exit(0);
                    499:        }
1.45      christos  500: #endif /* KERBEROS5 */
1.38      mycroft   501:
1.82      lukem     502:        init_curclass();
                    503:        curclass.timeout = 300;         /* 5 minutes, as per login(1) */
                    504:        curclass.type = CLASS_REAL;
                    505:
1.8       deraadt   506:        /* If logins are disabled, print out the message. */
1.100     lukem     507:        if (display_file(_PATH_NOLOGIN, 530)) {
1.8       deraadt   508:                reply(530, "System not available.");
                    509:                exit(0);
                    510:        }
1.100     lukem     511:        (void)display_file(conffilename(_PATH_FTPWELCOME), 220);
1.8       deraadt   512:                /* reply(220,) must follow */
1.101     lukem     513:        if (EMPTYSTR(version))
                    514:                reply(220, "%s FTP server ready.", hostname);
                    515:        else
                    516:                reply(220, "%s FTP server (%s) ready.", hostname, version);
1.73      lukem     517:
1.1       cgd       518:        (void) setjmp(errcatch);
                    519:        for (;;)
                    520:                (void) yyparse();
                    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:  *
                    656:  * For backward compatability, `allow' and `deny' are synonymns
                    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)
                   2073:                                reply(0, "PASV advertise address: %s", bp);
                   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.8       deraadt  2187: static void
1.88      lukem    2188: myoob(int signo)
1.1       cgd      2189: {
                   2190:        char *cp;
                   2191:
                   2192:        /* only process if transfer occurring */
                   2193:        if (!transflag)
                   2194:                return;
                   2195:        cp = tmpline;
                   2196:        if (getline(cp, 7, stdin) == NULL) {
                   2197:                reply(221, "You could at least say goodbye.");
                   2198:                dologout(0);
                   2199:        }
1.58      lukem    2200:        if (strcasecmp(cp, "ABOR\r\n") == 0) {
1.1       cgd      2201:                tmpline[0] = '\0';
                   2202:                reply(426, "Transfer aborted. Data connection closed.");
                   2203:                reply(226, "Abort successful");
                   2204:                longjmp(urgcatch, 1);
                   2205:        }
1.58      lukem    2206:        if (strcasecmp(cp, "STAT\r\n") == 0) {
1.78      lukem    2207:                tmpline[0] = '\0';
1.1       cgd      2208:                if (file_size != (off_t) -1)
1.109     lukem    2209:                        reply(213,
                   2210:                            "Status: " LLF " of " LLF " byte%s transferred",
                   2211:                            (LLT)byte_count, (LLT)file_size,
1.65      ross     2212:                            PLURAL(byte_count));
1.1       cgd      2213:                else
1.109     lukem    2214:                        reply(213, "Status: " LLF " byte%s transferred",
                   2215:                            (LLT)byte_count, PLURAL(byte_count));
1.1       cgd      2216:        }
                   2217: }
                   2218:
1.84      lukem    2219: static int
1.88      lukem    2220: bind_pasv_addr(void)
1.84      lukem    2221: {
                   2222:        static int passiveport;
                   2223:        int port, len;
                   2224:
                   2225:        len = pasv_addr.su_len;
                   2226:        if (curclass.portmin == 0 && curclass.portmax == 0) {
                   2227:                pasv_addr.su_port = 0;
1.109     lukem    2228:                return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len));
1.84      lukem    2229:        }
                   2230:
                   2231:        if (passiveport == 0) {
                   2232:                srand(getpid());
                   2233:                passiveport = rand() % (curclass.portmax - curclass.portmin)
                   2234:                    + curclass.portmin;
                   2235:        }
                   2236:
                   2237:        port = passiveport;
                   2238:        while (1) {
                   2239:                port++;
                   2240:                if (port > curclass.portmax)
                   2241:                        port = curclass.portmin;
                   2242:                else if (port == passiveport) {
                   2243:                        errno = EAGAIN;
                   2244:                        return (-1);
                   2245:                }
                   2246:                pasv_addr.su_port = htons(port);
1.109     lukem    2247:                if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0)
1.84      lukem    2248:                        break;
                   2249:                if (errno != EADDRINUSE)
                   2250:                        return (-1);
                   2251:        }
                   2252:        passiveport = port;
                   2253:        return (0);
                   2254: }
                   2255:
1.1       cgd      2256: /*
                   2257:  * Note: a response of 425 is not mentioned as a possible response to
1.8       deraadt  2258:  *     the PASV command in RFC959. However, it has been blessed as
                   2259:  *     a legitimate response by Jon Postel in a telephone conversation
1.1       cgd      2260:  *     with Rick Adams on 25 Jan 89.
                   2261:  */
1.8       deraadt  2262: void
1.88      lukem    2263: passive(void)
1.1       cgd      2264: {
                   2265:        int len;
1.8       deraadt  2266:        char *p, *a;
1.1       cgd      2267:
1.72      itojun   2268:        if (pdata >= 0)
                   2269:                close(pdata);
1.1       cgd      2270:        pdata = socket(AF_INET, SOCK_STREAM, 0);
1.23      lukem    2271:        if (pdata < 0 || !logged_in) {
1.1       cgd      2272:                perror_reply(425, "Can't open passive connection");
                   2273:                return;
                   2274:        }
                   2275:        pasv_addr = ctrl_addr;
1.84      lukem    2276:
                   2277:        if (bind_pasv_addr() < 0)
                   2278:                goto pasv_error;
1.67      itojun   2279:        len = pasv_addr.su_len;
1.109     lukem    2280:        if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
1.1       cgd      2281:                goto pasv_error;
1.109     lukem    2282:        pasv_addr.su_len = len;
1.1       cgd      2283:        if (listen(pdata, 1) < 0)
                   2284:                goto pasv_error;
1.118     lukem    2285:        if (curclass.advertise.su_len != 0)
                   2286:                a = (char *) &curclass.advertise.su_addr;
                   2287:        else
                   2288:                a = (char *) &pasv_addr.su_addr;
1.67      itojun   2289:        p = (char *) &pasv_addr.su_port;
1.1       cgd      2290:
                   2291: #define UC(b) (((int) b) & 0xff)
                   2292:
                   2293:        reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
                   2294:                UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
                   2295:        return;
                   2296:
1.89      lukem    2297:  pasv_error:
1.67      itojun   2298:        (void) close(pdata);
                   2299:        pdata = -1;
                   2300:        perror_reply(425, "Can't open passive connection");
                   2301:        return;
                   2302: }
                   2303:
                   2304: /*
1.106     itojun   2305:  * convert protocol identifier to/from AF
                   2306:  */
                   2307: int
                   2308: lpsvproto2af(int proto)
                   2309: {
                   2310:
                   2311:        switch (proto) {
1.109     lukem    2312:        case 4:
                   2313:                return AF_INET;
1.106     itojun   2314: #ifdef INET6
1.109     lukem    2315:        case 6:
                   2316:                return AF_INET6;
1.106     itojun   2317: #endif
1.109     lukem    2318:        default:
                   2319:                return -1;
1.106     itojun   2320:        }
                   2321: }
                   2322:
                   2323: int
                   2324: af2lpsvproto(int af)
                   2325: {
                   2326:
                   2327:        switch (af) {
1.109     lukem    2328:        case AF_INET:
                   2329:                return 4;
1.106     itojun   2330: #ifdef INET6
1.109     lukem    2331:        case AF_INET6:
                   2332:                return 6;
1.106     itojun   2333: #endif
1.109     lukem    2334:        default:
                   2335:                return -1;
1.106     itojun   2336:        }
                   2337: }
                   2338:
                   2339: int
                   2340: epsvproto2af(int proto)
                   2341: {
                   2342:
                   2343:        switch (proto) {
1.109     lukem    2344:        case 1:
                   2345:                return AF_INET;
1.106     itojun   2346: #ifdef INET6
1.109     lukem    2347:        case 2:
                   2348:                return AF_INET6;
1.106     itojun   2349: #endif
1.109     lukem    2350:        default:
                   2351:                return -1;
1.106     itojun   2352:        }
                   2353: }
                   2354:
                   2355: int
                   2356: af2epsvproto(int af)
                   2357: {
                   2358:
                   2359:        switch (af) {
1.109     lukem    2360:        case AF_INET:
                   2361:                return 1;
1.106     itojun   2362: #ifdef INET6
1.109     lukem    2363:        case AF_INET6:
                   2364:                return 2;
1.106     itojun   2365: #endif
1.109     lukem    2366:        default:
                   2367:                return -1;
1.106     itojun   2368:        }
                   2369: }
                   2370:
                   2371: /*
1.67      itojun   2372:  * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
                   2373:  * 229 Entering Extended Passive Mode (|||port|)
                   2374:  */
                   2375: void
                   2376: long_passive(char *cmd, int pf)
                   2377: {
                   2378:        int len;
1.84      lukem    2379:        char *p, *a;
1.67      itojun   2380:
                   2381:        if (!logged_in) {
                   2382:                syslog(LOG_NOTICE, "long passive but not logged in");
                   2383:                reply(503, "Login with USER first.");
                   2384:                return;
                   2385:        }
                   2386:
1.106     itojun   2387:        if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
                   2388:                /*
1.111     lukem    2389:                 * XXX: only EPRT/EPSV ready clients will understand this
1.106     itojun   2390:                 */
                   2391:                if (strcmp(cmd, "EPSV") != 0)
                   2392:                        reply(501, "Network protocol mismatch"); /*XXX*/
                   2393:                else
                   2394:                        epsv_protounsupp("Network protocol mismatch");
1.67      itojun   2395:
1.106     itojun   2396:                return;
1.67      itojun   2397:        }
1.73      lukem    2398:
1.72      itojun   2399:        if (pdata >= 0)
                   2400:                close(pdata);
1.67      itojun   2401:        pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
                   2402:        if (pdata < 0) {
                   2403:                perror_reply(425, "Can't open passive connection");
                   2404:                return;
                   2405:        }
                   2406:        pasv_addr = ctrl_addr;
1.84      lukem    2407:        if (bind_pasv_addr() < 0)
1.67      itojun   2408:                goto pasv_error;
                   2409:        len = pasv_addr.su_len;
1.109     lukem    2410:        if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
1.67      itojun   2411:                goto pasv_error;
1.109     lukem    2412:        pasv_addr.su_len = len;
1.67      itojun   2413:        if (listen(pdata, 1) < 0)
                   2414:                goto pasv_error;
                   2415:        p = (char *) &pasv_addr.su_port;
                   2416:
                   2417: #define UC(b) (((int) b) & 0xff)
                   2418:
                   2419:        if (strcmp(cmd, "LPSV") == 0) {
1.118     lukem    2420:                struct sockinet *advert;
                   2421:
                   2422:                if (curclass.advertise.su_len != 0)
                   2423:                        advert = &curclass.advertise;
                   2424:                else
                   2425:                        advert = &pasv_addr;
                   2426:                switch (advert->su_family) {
1.67      itojun   2427:                case AF_INET:
1.118     lukem    2428:                        a = (char *) &advert->su_addr;
1.109     lukem    2429:                        reply(228,
                   2430:     "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
1.67      itojun   2431:                                4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
                   2432:                                2, UC(p[0]), UC(p[1]));
                   2433:                        return;
1.104     christos 2434: #ifdef INET6
1.67      itojun   2435:                case AF_INET6:
1.118     lukem    2436:                        a = (char *) &advert->su_6addr;
1.109     lukem    2437:                        reply(228,
                   2438:     "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   2439:                                6, 16,
                   2440:                                UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
                   2441:                                UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
                   2442:                                UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
                   2443:                                UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
                   2444:                                2, UC(p[0]), UC(p[1]));
                   2445:                        return;
1.104     christos 2446: #endif
1.67      itojun   2447:                }
                   2448: #undef UC
                   2449:        } else if (strcmp(cmd, "EPSV") == 0) {
                   2450:                switch (pasv_addr.su_family) {
                   2451:                case AF_INET:
1.109     lukem    2452: #ifdef INET6
1.67      itojun   2453:                case AF_INET6:
1.109     lukem    2454: #endif
1.67      itojun   2455:                        reply(229, "Entering Extended Passive Mode (|||%d|)",
1.109     lukem    2456:                            ntohs(pasv_addr.su_port));
1.67      itojun   2457:                        return;
                   2458:                }
                   2459:        } else {
                   2460:                /* more proper error code? */
                   2461:        }
                   2462:
1.89      lukem    2463:  pasv_error:
1.1       cgd      2464:        (void) close(pdata);
                   2465:        pdata = -1;
                   2466:        perror_reply(425, "Can't open passive connection");
                   2467:        return;
1.106     itojun   2468: }
                   2469:
                   2470: int
                   2471: extended_port(const char *arg)
                   2472: {
                   2473:        char *tmp = NULL;
                   2474:        char *result[3];
                   2475:        char *p, *q;
                   2476:        char delim;
                   2477:        struct addrinfo hints;
                   2478:        struct addrinfo *res = NULL;
                   2479:        int i;
                   2480:        unsigned long proto;
                   2481:
                   2482:        tmp = xstrdup(arg);
                   2483:        p = tmp;
                   2484:        delim = p[0];
                   2485:        p++;
                   2486:        memset(result, 0, sizeof(result));
                   2487:        for (i = 0; i < 3; i++) {
                   2488:                q = strchr(p, delim);
                   2489:                if (!q || *q != delim)
                   2490:                        goto parsefail;
                   2491:                *q++ = '\0';
                   2492:                result[i] = p;
                   2493:                p = q;
                   2494:        }
                   2495:
1.111     lukem    2496:                        /* some more sanity checks */
1.106     itojun   2497:        p = NULL;
                   2498:        (void)strtoul(result[2], &p, 10);
                   2499:        if (!*result[2] || *p)
1.108     itojun   2500:                goto parsefail;
1.106     itojun   2501:        p = NULL;
                   2502:        proto = strtoul(result[0], &p, 10);
                   2503:        if (!*result[0] || *p)
                   2504:                goto protounsupp;
                   2505:
                   2506:        memset(&hints, 0, sizeof(hints));
                   2507:        hints.ai_family = epsvproto2af((int)proto);
                   2508:        if (hints.ai_family < 0)
                   2509:                goto protounsupp;
                   2510:        hints.ai_socktype = SOCK_STREAM;
                   2511:        hints.ai_flags = AI_NUMERICHOST;
                   2512:        if (getaddrinfo(result[1], result[2], &hints, &res))
                   2513:                goto parsefail;
                   2514:        if (res->ai_next)
                   2515:                goto parsefail;
                   2516:        if (sizeof(data_dest) < res->ai_addrlen)
                   2517:                goto parsefail;
1.118     lukem    2518:        memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen);
                   2519:        data_dest.su_len = res->ai_addrlen;
1.113     lukem    2520: #ifdef INET6
1.106     itojun   2521:        if (his_addr.su_family == AF_INET6 &&
                   2522:            data_dest.su_family == AF_INET6) {
1.111     lukem    2523:                        /* XXX: more sanity checks! */
1.109     lukem    2524:                data_dest.su_scope_id = his_addr.su_scope_id;
1.106     itojun   2525:        }
1.113     lukem    2526: #endif
1.106     itojun   2527:
                   2528:        if (tmp != NULL)
                   2529:                free(tmp);
                   2530:        if (res)
                   2531:                freeaddrinfo(res);
                   2532:        return 0;
                   2533:
1.109     lukem    2534:  parsefail:
1.106     itojun   2535:        reply(500, "Invalid argument, rejected.");
                   2536:        usedefault = 1;
1.107     itojun   2537:        if (tmp != NULL)
                   2538:                free(tmp);
                   2539:        if (res)
                   2540:                freeaddrinfo(res);
1.106     itojun   2541:        return -1;
                   2542:
1.109     lukem    2543:  protounsupp:
1.106     itojun   2544:        epsv_protounsupp("Protocol not supported");
                   2545:        usedefault = 1;
1.107     itojun   2546:        if (tmp != NULL)
                   2547:                free(tmp);
                   2548:        if (res)
                   2549:                freeaddrinfo(res);
1.106     itojun   2550:        return -1;
                   2551: }
                   2552:
                   2553: /*
                   2554:  * 522 Protocol not supported (proto,...)
                   2555:  * as we assume address family for control and data connections are the same,
                   2556:  * we do not return the list of address families we support - instead, we
                   2557:  * return the address family of the control connection.
                   2558:  */
                   2559: void
                   2560: epsv_protounsupp(const char *message)
                   2561: {
                   2562:        int proto;
                   2563:
                   2564:        proto = af2epsvproto(ctrl_addr.su_family);
                   2565:        if (proto < 0)
1.111     lukem    2566:                reply(501, "%s", message);      /* XXX */
1.106     itojun   2567:        else
                   2568:                reply(522, "%s, use (%d)", message, proto);
1.1       cgd      2569: }
                   2570:
                   2571: /*
                   2572:  * Generate unique name for file with basename "local".
                   2573:  * The file named "local" is already known to exist.
                   2574:  * Generates failure reply on error.
1.29      mrg      2575:  *
1.109     lukem    2576:  * XXX:        this function should under go changes similar to
                   2577:  *     the mktemp(3)/mkstemp(3) changes.
1.1       cgd      2578:  */
1.8       deraadt  2579: static char *
1.88      lukem    2580: gunique(const char *local)
1.1       cgd      2581: {
1.82      lukem    2582:        static char new[MAXPATHLEN];
1.1       cgd      2583:        struct stat st;
1.8       deraadt  2584:        char *cp;
1.73      lukem    2585:        int count;
1.1       cgd      2586:
1.8       deraadt  2587:        cp = strrchr(local, '/');
1.1       cgd      2588:        if (cp)
                   2589:                *cp = '\0';
                   2590:        if (stat(cp ? local : ".", &st) < 0) {
                   2591:                perror_reply(553, cp ? local : ".");
1.57      lukem    2592:                return (NULL);
1.1       cgd      2593:        }
                   2594:        if (cp)
                   2595:                *cp = '/';
                   2596:        for (count = 1; count < 100; count++) {
1.73      lukem    2597:                (void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count);
1.1       cgd      2598:                if (stat(new, &st) < 0)
1.8       deraadt  2599:                        return (new);
1.1       cgd      2600:        }
                   2601:        reply(452, "Unique file name cannot be created.");
1.8       deraadt  2602:        return (NULL);
1.1       cgd      2603: }
                   2604:
                   2605: /*
                   2606:  * Format and send reply containing system error number.
                   2607:  */
1.8       deraadt  2608: void
1.88      lukem    2609: perror_reply(int code, const char *string)
1.1       cgd      2610: {
1.62      lukem    2611:        int save_errno;
1.8       deraadt  2612:
1.62      lukem    2613:        save_errno = errno;
1.1       cgd      2614:        reply(code, "%s: %s.", string, strerror(errno));
1.62      lukem    2615:        errno = save_errno;
1.1       cgd      2616: }
                   2617:
                   2618: static char *onefile[] = {
                   2619:        "",
                   2620:        0
                   2621: };
                   2622:
1.8       deraadt  2623: void
1.88      lukem    2624: send_file_list(const char *whichf)
1.1       cgd      2625: {
                   2626:        struct stat st;
                   2627:        DIR *dirp = NULL;
                   2628:        struct dirent *dir;
                   2629:        FILE *dout = NULL;
1.62      lukem    2630:        char **dirlist, *dirname, *p;
1.1       cgd      2631:        int simple = 0;
1.8       deraadt  2632:        int freeglob = 0;
                   2633:        glob_t gl;
1.62      lukem    2634:
1.25      christos 2635: #ifdef __GNUC__
                   2636:        (void) &dout;
                   2637:        (void) &dirlist;
                   2638:        (void) &simple;
                   2639:        (void) &freeglob;
                   2640: #endif
1.1       cgd      2641:
1.62      lukem    2642:        p = NULL;
1.8       deraadt  2643:        if (strpbrk(whichf, "~{[*?") != NULL) {
1.122   ! christos 2644:                int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
1.1       cgd      2645:
1.8       deraadt  2646:                memset(&gl, 0, sizeof(gl));
                   2647:                freeglob = 1;
                   2648:                if (glob(whichf, flags, 0, &gl)) {
                   2649:                        reply(550, "not found");
                   2650:                        goto out;
                   2651:                } else if (gl.gl_pathc == 0) {
1.1       cgd      2652:                        errno = ENOENT;
1.8       deraadt  2653:                        perror_reply(550, whichf);
                   2654:                        goto out;
1.1       cgd      2655:                }
1.8       deraadt  2656:                dirlist = gl.gl_pathv;
1.1       cgd      2657:        } else {
1.62      lukem    2658:                p = xstrdup(whichf);
                   2659:                onefile[0] = p;
1.1       cgd      2660:                dirlist = onefile;
                   2661:                simple = 1;
                   2662:        }
1.62      lukem    2663:                                        /* XXX: } for vi sm */
1.1       cgd      2664:
                   2665:        if (setjmp(urgcatch)) {
                   2666:                transflag = 0;
1.8       deraadt  2667:                goto out;
1.1       cgd      2668:        }
1.20      lukem    2669:        while ((dirname = *dirlist++) != NULL) {
1.33      lukem    2670:                int trailingslash = 0;
                   2671:
1.1       cgd      2672:                if (stat(dirname, &st) < 0) {
                   2673:                        /*
                   2674:                         * If user typed "ls -l", etc, and the client
                   2675:                         * used NLST, do what the user meant.
                   2676:                         */
1.71      lukem    2677:                        /* XXX: nuke this support? */
1.1       cgd      2678:                        if (dirname[0] == '-' && *dirlist == NULL &&
                   2679:                            transflag == 0) {
1.71      lukem    2680:                                char *argv[] = { INTERNAL_LS, "", NULL };
                   2681:
                   2682:                                argv[1] = dirname;
                   2683:                                retrieve(argv, dirname);
1.8       deraadt  2684:                                goto out;
1.1       cgd      2685:                        }
1.8       deraadt  2686:                        perror_reply(550, whichf);
1.95      lukem    2687:                        goto cleanup_send_file_list;
1.1       cgd      2688:                }
                   2689:
1.8       deraadt  2690:                if (S_ISREG(st.st_mode)) {
1.118     lukem    2691:                        /*
                   2692:                         * XXXRFC:
                   2693:                         *      should we follow RFC959 and not work
                   2694:                         *      for non directories?
                   2695:                         */
1.1       cgd      2696:                        if (dout == NULL) {
                   2697:                                dout = dataconn("file list", (off_t)-1, "w");
                   2698:                                if (dout == NULL)
1.8       deraadt  2699:                                        goto out;
1.1       cgd      2700:                                transflag++;
                   2701:                        }
1.118     lukem    2702:                        cprintf(dout, "%s%s\n", dirname,
1.62      lukem    2703:                            type == TYPE_A ? "\r" : "");
1.1       cgd      2704:                        byte_count += strlen(dirname) + 1;
                   2705:                        continue;
1.8       deraadt  2706:                } else if (!S_ISDIR(st.st_mode))
1.1       cgd      2707:                        continue;
                   2708:
1.33      lukem    2709:                if (dirname[strlen(dirname) - 1] == '/')
                   2710:                        trailingslash++;
                   2711:
1.1       cgd      2712:                if ((dirp = opendir(dirname)) == NULL)
                   2713:                        continue;
                   2714:
                   2715:                while ((dir = readdir(dirp)) != NULL) {
1.82      lukem    2716:                        char nbuf[MAXPATHLEN];
1.1       cgd      2717:
1.93      lukem    2718:                        if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
1.1       cgd      2719:                                continue;
                   2720:
1.33      lukem    2721:                        (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
                   2722:                            trailingslash ? "" : "/", dir->d_name);
1.1       cgd      2723:
                   2724:                        /*
1.30      lukem    2725:                         * We have to do a stat to ensure it's
1.1       cgd      2726:                         * not a directory or special file.
                   2727:                         */
1.118     lukem    2728:                        /*
                   2729:                         * XXXRFC:
                   2730:                         *      should we follow RFC959 and filter out
                   2731:                         *      non files ?   lukem - NO!, or not until
                   2732:                         *      our ftp client uses MLS{T,D} for completion.
                   2733:                         */
1.1       cgd      2734:                        if (simple || (stat(nbuf, &st) == 0 &&
1.8       deraadt  2735:                            S_ISREG(st.st_mode))) {
1.62      lukem    2736:                                char *p;
                   2737:
1.1       cgd      2738:                                if (dout == NULL) {
                   2739:                                        dout = dataconn("file list", (off_t)-1,
                   2740:                                                "w");
                   2741:                                        if (dout == NULL)
1.8       deraadt  2742:                                                goto out;
1.1       cgd      2743:                                        transflag++;
                   2744:                                }
1.62      lukem    2745:                                p = nbuf;
1.1       cgd      2746:                                if (nbuf[0] == '.' && nbuf[1] == '/')
1.62      lukem    2747:                                        p = &nbuf[2];
1.118     lukem    2748:                                cprintf(dout, "%s%s\n", p,
1.62      lukem    2749:                                    type == TYPE_A ? "\r" : "");
1.1       cgd      2750:                                byte_count += strlen(nbuf) + 1;
                   2751:                        }
                   2752:                }
                   2753:                (void) closedir(dirp);
                   2754:        }
                   2755:
                   2756:        if (dout == NULL)
                   2757:                reply(550, "No files found.");
                   2758:        else if (ferror(dout) != 0)
                   2759:                perror_reply(550, "Data connection");
                   2760:        else
                   2761:                reply(226, "Transfer complete.");
                   2762:
1.95      lukem    2763:  cleanup_send_file_list:
1.1       cgd      2764:        transflag = 0;
1.95      lukem    2765:        closedataconn(dout);
1.89      lukem    2766:  out:
1.62      lukem    2767:        total_xfers++;
                   2768:        total_xfers_out++;
                   2769:        if (p)
                   2770:                free(p);
                   2771:        if (freeglob)
1.8       deraadt  2772:                globfree(&gl);
1.34      lukem    2773: }
                   2774:
                   2775: char *
1.88      lukem    2776: conffilename(const char *s)
1.34      lukem    2777: {
1.82      lukem    2778:        static char filename[MAXPATHLEN];
1.34      lukem    2779:
1.73      lukem    2780:        if (*s == '/')
                   2781:                strlcpy(filename, s, sizeof(filename));
                   2782:        else
                   2783:                (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
                   2784:        return (filename);
1.62      lukem    2785: }
                   2786:
                   2787: /*
1.118     lukem    2788:  * logxfer --
                   2789:  *     if logging > 1, then based on the arguments, syslog a message:
1.62      lukem    2790:  *      if bytes != -1         "<command> <file1> = <bytes> bytes"
                   2791:  *      else if file2 != NULL  "<command> <file1> <file2>"
                   2792:  *      else                   "<command> <file1>"
                   2793:  *     if elapsed != NULL, append "in xxx.yyy seconds"
                   2794:  *     if error != NULL, append ": " + error
1.118     lukem    2795:  *
                   2796:  *     if doxferlog != 0, syslog a wu-ftpd style xferlog entry
1.62      lukem    2797:  */
                   2798: void
1.118     lukem    2799: logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
1.88      lukem    2800:        const struct timeval *elapsed, const char *error)
1.62      lukem    2801: {
1.118     lukem    2802:        char             buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN];
                   2803:        const char      *r1, *r2;
                   2804:        char             direction;
                   2805:        size_t           len;
                   2806:        time_t           now;
1.62      lukem    2807:
1.118     lukem    2808:        if (logging <=1 && !doxferlog)
1.62      lukem    2809:                return;
                   2810:
1.118     lukem    2811:        r1 = r2 = NULL;
                   2812:        if ((r1 = realpath(file1, realfile)) == NULL)
                   2813:                r1 = file1;
                   2814:        if (file2 != NULL)
                   2815:                if ((r2 = realpath(file2, realfile)) == NULL)
                   2816:                        r2 = file2;
1.62      lukem    2817:
1.118     lukem    2818:                /*
                   2819:                 * syslog command
                   2820:                 */
                   2821:        if (logging > 1) {
                   2822:                len = snprintf(buf, sizeof(buf), "%s %s", command, r1);
                   2823:                if (bytes != (off_t)-1)
                   2824:                        len += snprintf(buf + len, sizeof(buf) - len,
                   2825:                            " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes));
                   2826:                else if (r2 != NULL)
                   2827:                        len += snprintf(buf + len, sizeof(buf) - len,
                   2828:                            " %s", r2);
                   2829:                if (elapsed != NULL)
                   2830:                        len += snprintf(buf + len, sizeof(buf) - len,
                   2831:                            " in %ld.%.03d seconds", elapsed->tv_sec,
                   2832:                            (int)(elapsed->tv_usec / 1000));
                   2833:                if (error != NULL)
                   2834:                        len += snprintf(buf + len, sizeof(buf) - len,
                   2835:                            ": %s", error);
                   2836:                syslog(LOG_INFO, "%s", buf);
1.62      lukem    2837:        }
                   2838:
                   2839:
1.118     lukem    2840:                /*
                   2841:                 * syslog wu-ftpd style log entry, prefixed with "xferlog: "
                   2842:                 */
                   2843:        if (!doxferlog)
                   2844:                return;
                   2845:
                   2846:        if (strcmp(command, "get") == 0)
                   2847:                direction = 'o';
                   2848:        else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0)
                   2849:                direction = 'i';
                   2850:        else
                   2851:                return;
1.62      lukem    2852:
1.118     lukem    2853:        time(&now);
                   2854:        syslog(LOG_INFO,
                   2855:            "xferlog%s: %.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c",
                   2856:
                   2857: /*
                   2858:  * XXX: wu-ftpd puts (send) or (recv) in the syslog message, and removes
                   2859:  *     the full date.  This may be problematic for accurate log parsing,
                   2860:  *     given that syslog messages don't contain the full date.
                   2861:  */
                   2862: #if 1          /* lukem's method; easier to convert to actual xferlog file */
                   2863:            "",
                   2864:            ctime(&now),
                   2865: #else          /* wu-ftpd's syslog method, with an extra unneeded space */
                   2866:            (direction == 'i') ? " (recv)" : " (send)",
                   2867:            "",
                   2868: #endif
                   2869:            elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0),
                   2870:            remotehost,
                   2871:            bytes == (off_t)-1 ? 0 : (LLT) bytes,
                   2872:            r1,
                   2873:            type == TYPE_A ? 'a' : 'b',
                   2874:            "_",                /* XXX: take conversions into account? */
                   2875:            direction,
                   2876:
                   2877:            curclass.type == CLASS_GUEST ?  'a' :
                   2878:            curclass.type == CLASS_CHROOT ? 'g' :
                   2879:            curclass.type == CLASS_REAL ?   'r' : '?',
                   2880:
                   2881:            curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
                   2882:            error != NULL ? 'i' : 'c'
                   2883:            );
1.115     lukem    2884: }
                   2885:
                   2886: /*
1.116     lukem    2887:  * Determine if `password' is valid for user given in `pw'.
                   2888:  * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
1.115     lukem    2889:  */
                   2890: int
1.116     lukem    2891: checkpassword(const struct passwd *pw, const char *password)
1.115     lukem    2892: {
1.116     lukem    2893:        char    *orig, *new;
                   2894:        time_t   expire;
                   2895:
                   2896:        expire = 0;
                   2897:        if (pw == NULL)
                   2898:                return 1;
                   2899:
                   2900:        orig = pw->pw_passwd;           /* save existing password */
                   2901:        expire = pw->pw_expire;
                   2902:
                   2903:        if (orig[0] == '\0')            /* don't allow empty passwords */
                   2904:                return 1;
                   2905:
                   2906:        new = crypt(password, orig);    /* encrypt given password */
                   2907:        if (strcmp(new, orig) != 0)     /* compare */
                   2908:                return 1;
                   2909:
                   2910:        if (expire && time(NULL) >= expire)
                   2911:                return 2;               /* check if expired */
1.115     lukem    2912:
1.116     lukem    2913:        return 0;                       /* OK! */
1.62      lukem    2914: }
                   2915:
                   2916: char *
1.88      lukem    2917: xstrdup(const char *s)
1.62      lukem    2918: {
                   2919:        char *new = strdup(s);
                   2920:
                   2921:        if (new == NULL)
                   2922:                fatal("Local resource failure: malloc");
                   2923:                /* NOTREACHED */
                   2924:        return (new);
1.95      lukem    2925: }
                   2926:
                   2927: /*
                   2928:  * As per fprintf(), but increment total_bytes and total_bytes_out,
                   2929:  * by the appropriate amount.
                   2930:  */
                   2931: void
                   2932: cprintf(FILE *fd, const char *fmt, ...)
                   2933: {
                   2934:        off_t b;
                   2935:        va_list ap;
                   2936:
                   2937:        va_start(ap, fmt);
                   2938:        b = vfprintf(fd, fmt, ap);
                   2939:        total_bytes += b;
                   2940:        total_bytes_out += b;
1.1       cgd      2941: }

CVSweb <webmaster@jp.NetBSD.org>