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>