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