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