ftpd.c revision 1.58 1 /* $NetBSD: ftpd.c,v 1.58 1998/09/07 08:11:20 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
46 #else
47 __RCSID("$NetBSD: ftpd.c,v 1.58 1998/09/07 08:11:20 lukem Exp $");
48 #endif
49 #endif /* not lint */
50
51 /*
52 * FTP server.
53 */
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/wait.h>
59
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63
64 #define FTP_NAMES
65 #include <arpa/ftp.h>
66 #include <arpa/inet.h>
67 #include <arpa/telnet.h>
68
69 #include <ctype.h>
70 #include <dirent.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <fnmatch.h>
75 #include <glob.h>
76 #include <limits.h>
77 #include <netdb.h>
78 #include <pwd.h>
79 #include <setjmp.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <syslog.h>
85 #include <time.h>
86 #include <unistd.h>
87 #ifdef SKEY
88 #include <skey.h>
89 #endif
90 #ifdef KERBEROS5
91 #include <krb5.h>
92 #endif
93
94 #include "extern.h"
95 #include "pathnames.h"
96
97 #if __STDC__
98 #include <stdarg.h>
99 #else
100 #include <varargs.h>
101 #endif
102
103 #ifndef TRUE
104 #define TRUE 1
105 #define FALSE 0
106 #endif
107
108 const char version[] = "Version: 7.03";
109
110 struct sockaddr_in ctrl_addr;
111 struct sockaddr_in data_source;
112 struct sockaddr_in data_dest;
113 struct sockaddr_in his_addr;
114 struct sockaddr_in pasv_addr;
115
116 int data;
117 jmp_buf errcatch, urgcatch;
118 int logged_in;
119 struct passwd *pw;
120 int debug;
121 int sflag;
122 int logging;
123 int guest;
124 int dochroot;
125 int type;
126 int form;
127 int stru; /* avoid C keyword */
128 int mode;
129 int usedefault = 1; /* for data transfers */
130 int pdata = -1; /* for passive mode */
131 sig_atomic_t transflag;
132 off_t file_size;
133 off_t byte_count;
134 char tmpline[7];
135 char hostname[MAXHOSTNAMELEN+1];
136 char remotehost[MAXHOSTNAMELEN+1];
137 static char ttyline[20];
138 char *tty = ttyline; /* for klogin */
139
140 static char *anondir = NULL;
141 static char confdir[MAXPATHLEN];
142
143 #if defined(KERBEROS) || defined(KERBEROS5)
144 int notickets = 1;
145 char *krbtkfile_env = NULL;
146 #endif
147
148 /*
149 * Timeout intervals for retrying connections
150 * to hosts that don't accept PORT cmds. This
151 * is a kludge, but given the problems with TCP...
152 */
153 #define SWAITMAX 90 /* wait at most 90 seconds */
154 #define SWAITINT 5 /* interval between retries */
155
156 int swaitmax = SWAITMAX;
157 int swaitint = SWAITINT;
158
159 #ifdef HASSETPROCTITLE
160 char proctitle[BUFSIZ]; /* initial part of title */
161 #endif /* HASSETPROCTITLE */
162
163 #define LOGCMD(cmd, file) \
164 if (logging > 1) \
165 syslog(LOG_INFO,"%s %s%s", cmd, \
166 *(file) == '/' ? "" : curdir(), file);
167 #define LOGCMD2(cmd, file1, file2) \
168 if (logging > 1) \
169 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
170 *(file1) == '/' ? "" : curdir(), file1, \
171 *(file2) == '/' ? "" : curdir(), file2);
172 #define LOGBYTES(cmd, file, cnt) \
173 if (logging > 1) { \
174 if (cnt == (off_t)-1) \
175 syslog(LOG_INFO,"%s %s%s", cmd, \
176 *(file) == '/' ? "" : curdir(), file); \
177 else \
178 syslog(LOG_INFO, "%s %s%s = %qd bytes", \
179 cmd, (*(file) == '/') ? "" : curdir(), file, \
180 (long long)cnt); \
181 }
182
183 static void ack __P((char *));
184 static void myoob __P((int));
185 static int checkuser __P((const char *, const char *, int, int));
186 static int checkaccess __P((const char *));
187 static FILE *dataconn __P((char *, off_t, char *));
188 static void dolog __P((struct sockaddr_in *));
189 static char *curdir __P((void));
190 static void end_login __P((void));
191 static FILE *getdatasock __P((char *));
192 static char *gunique __P((char *));
193 static void lostconn __P((int));
194 static int receive_data __P((FILE *, FILE *));
195 static void replydirname __P((const char *, const char *));
196 static int send_data __P((FILE *, FILE *, off_t));
197 static struct passwd *
198 sgetpwnam __P((const char *));
199
200 int main __P((int, char *[]));
201
202 #if defined(KERBEROS) || defined(KERBEROS5)
203 int klogin __P((struct passwd *, char *, char *, char *));
204 void kdestroy __P((void));
205 #endif
206
207 static char *
208 curdir()
209 {
210 static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */
211
212 if (getcwd(path, sizeof(path)-2) == NULL)
213 return ("");
214 if (path[1] != '\0') /* special case for root dir. */
215 strcat(path, "/");
216 /* For guest account, skip / since it's chrooted */
217 return (guest ? path+1 : path);
218 }
219
220 int
221 main(argc, argv)
222 int argc;
223 char *argv[];
224 {
225 int addrlen, ch, on = 1, tos;
226 char *cp, line[LINE_MAX];
227 FILE *fd;
228 #ifdef KERBEROS5
229 krb5_error_code kerror;
230 #endif
231
232 debug = 0;
233 logging = 0;
234 sflag = 0;
235 (void)strcpy(confdir, _DEFAULT_CONFDIR);
236
237 while ((ch = getopt(argc, argv, "a:c:C:dlst:T:u:v")) != -1) {
238 switch (ch) {
239 case 'a':
240 anondir = optarg;
241 break;
242
243 case 'c':
244 (void)strncpy(confdir, optarg, sizeof(confdir));
245 confdir[sizeof(confdir)-1] = '\0';
246 break;
247
248 case 'C':
249 exit(checkaccess(optarg));
250 /* NOTREACHED */
251
252 case 'd':
253 case 'v': /* deprecated */
254 debug = 1;
255 break;
256
257 case 'l':
258 logging++; /* > 1 == extra logging */
259 break;
260
261 case 's':
262 sflag = 1;
263 break;
264
265 case 't':
266 case 'T':
267 case 'u':
268 warnx("-%c has been deprecated in favour of ftpd.conf",
269 ch);
270 break;
271
272 default:
273 if (optopt == 'a' || optopt == 'C')
274 exit(1);
275 warnx("unknown flag -%c ignored", optopt);
276 break;
277 }
278 }
279
280 /*
281 * LOG_NDELAY sets up the logging connection immediately,
282 * necessary for anonymous ftp's that chroot and can't do it later.
283 */
284 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
285 addrlen = sizeof(his_addr);
286 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
287 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
288 exit(1);
289 }
290 addrlen = sizeof(ctrl_addr);
291 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
292 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
293 exit(1);
294 }
295 #ifdef IP_TOS
296 tos = IPTOS_LOWDELAY;
297 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
298 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
299 #endif
300 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
301
302 /* set this here so klogin can use it... */
303 (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
304
305 (void) freopen(_PATH_DEVNULL, "w", stderr);
306 (void) signal(SIGPIPE, lostconn);
307 (void) signal(SIGCHLD, SIG_IGN);
308 if ((long)signal(SIGURG, myoob) < 0)
309 syslog(LOG_ERR, "signal: %m");
310
311 /* Try to handle urgent data inline */
312 #ifdef SO_OOBINLINE
313 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
314 syslog(LOG_ERR, "setsockopt: %m");
315 #endif
316
317 #ifdef F_SETOWN
318 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
319 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
320 #endif
321 dolog(&his_addr);
322 /*
323 * Set up default state
324 */
325 data = -1;
326 type = TYPE_A;
327 form = FORM_N;
328 stru = STRU_F;
329 mode = MODE_S;
330 tmpline[0] = '\0';
331 hasyyerrored = 0;
332
333 #ifdef KERBEROS5
334 kerror = krb5_init_context(&kcontext);
335 if (kerror) {
336 syslog(LOG_NOTICE, "%s when initializing Kerberos context",
337 error_message(kerror));
338 exit(0);
339 }
340 #endif /* KERBEROS5 */
341
342 /* If logins are disabled, print out the message. */
343 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
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, "System not available.");
352 exit(0);
353 }
354 if ((fd = fopen(conffilename(_PATH_FTPWELCOME), "r")) != NULL) {
355 while (fgets(line, sizeof(line), fd) != NULL) {
356 if ((cp = strchr(line, '\n')) != NULL)
357 *cp = '\0';
358 lreply(220, "%s", line);
359 }
360 (void) fflush(stdout);
361 (void) fclose(fd);
362 /* reply(220,) must follow */
363 }
364 (void)gethostname(hostname, sizeof(hostname));
365 hostname[sizeof(hostname) - 1] = '\0';
366 reply(220, "%s FTP server (%s) ready.", hostname, version);
367 curclass.timeout = 300; /* 5 minutes, as per login(1) */
368 (void) setjmp(errcatch);
369 for (;;)
370 (void) yyparse();
371 /* NOTREACHED */
372 }
373
374 static void
375 lostconn(signo)
376 int signo;
377 {
378
379 if (debug)
380 syslog(LOG_DEBUG, "lost connection");
381 dologout(1);
382 }
383
384 /*
385 * Save the result of a getpwnam. Used for USER command, since
386 * the data returned must not be clobbered by any other command
387 * (e.g., globbing).
388 */
389 static struct passwd *
390 sgetpwnam(name)
391 const char *name;
392 {
393 static struct passwd save;
394 struct passwd *p;
395
396 if ((p = getpwnam(name)) == NULL)
397 return (p);
398 if (save.pw_name) {
399 free((char *)save.pw_name);
400 free((char *)save.pw_passwd);
401 free((char *)save.pw_gecos);
402 free((char *)save.pw_dir);
403 free((char *)save.pw_shell);
404 }
405 save = *p;
406 save.pw_name = xstrdup(p->pw_name);
407 save.pw_passwd = xstrdup(p->pw_passwd);
408 save.pw_gecos = xstrdup(p->pw_gecos);
409 save.pw_dir = xstrdup(p->pw_dir);
410 save.pw_shell = xstrdup(p->pw_shell);
411 return (&save);
412 }
413
414 static int login_attempts; /* number of failed login attempts */
415 static int askpasswd; /* had user command, ask for passwd */
416 static char curname[10]; /* current USER name */
417
418 /*
419 * USER command.
420 * Sets global passwd pointer pw if named account exists and is acceptable;
421 * sets askpasswd if a PASS command is expected. If logged in previously,
422 * need to reset state. If name is "ftp" or "anonymous", the name is not in
423 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
424 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
425 * requesting login privileges. Disallow anyone who does not have a standard
426 * shell as returned by getusershell(). Disallow anyone mentioned in the file
427 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
428 */
429 void
430 user(name)
431 char *name;
432 {
433 if (logged_in) {
434 if (guest) {
435 reply(530, "Can't change user from guest login.");
436 return;
437 } else if (dochroot) {
438 reply(530, "Can't change user from chroot user.");
439 return;
440 }
441 end_login();
442 }
443
444 #if defined(KERBEROS) || defined(KERBEROS5)
445 kdestroy();
446 #endif
447
448 guest = 0;
449 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
450 if (checkaccess("ftp") || checkaccess("anonymous"))
451 reply(530, "User %s access denied.", name);
452 else if ((pw = sgetpwnam("ftp")) != NULL) {
453 guest = 1;
454 askpasswd = 1;
455 reply(331,
456 "Guest login ok, type your name as password.");
457 } else
458 reply(530, "User %s unknown.", name);
459 if (!askpasswd && logging)
460 syslog(LOG_NOTICE,
461 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
462 return;
463 }
464
465 pw = sgetpwnam(name);
466 if (logging)
467 strncpy(curname, name, sizeof(curname)-1);
468
469 #ifdef SKEY
470 if (skey_haskey(name) == 0) {
471 char *myskey;
472
473 myskey = skey_keyinfo(name);
474 reply(331, "Password [%s] required for %s.",
475 myskey ? myskey : "error getting challenge", name);
476 } else
477 #endif
478 reply(331, "Password required for %s.", name);
479
480 askpasswd = 1;
481 /*
482 * Delay before reading passwd after first failed
483 * attempt to slow down passwd-guessing programs.
484 */
485 if (login_attempts)
486 sleep((unsigned) login_attempts);
487 }
488
489 /*
490 * Determine whether something is to happen (allow access, chroot)
491 * for a user. Each line is a shell-style glob followed by
492 * `yes' or `no'.
493 *
494 * For backward compatability, `allow' and `deny' are synonymns
495 * for `yes' and `no', respectively.
496 *
497 * Each glob is matched against the username in turn, and the first
498 * match found is used. If no match is found, the result is the
499 * argument `def'. If a match is found but without and explicit
500 * `yes'/`no', the result is the opposite of def.
501 *
502 * If the file doesn't exist at all, the result is the argument
503 * `nofile'
504 *
505 * Any line starting with `#' is considered a comment and ignored.
506 *
507 * Returns FALSE if the user is denied, or TRUE if they are allowed.
508 */
509 int
510 checkuser(fname, name, def, nofile)
511 const char *fname, *name;
512 int def, nofile;
513 {
514 FILE *fd;
515 int retval;
516 char *glob, *perm, line[BUFSIZ];
517
518 retval = def;
519 if ((fd = fopen(conffilename(fname), "r")) == NULL)
520 return nofile;
521
522 while (fgets(line, sizeof(line), fd) != NULL) {
523 glob = strtok(line, " \t\n");
524 if (glob[0] == '#')
525 continue;
526 perm = strtok(NULL, " \t\n");
527 if (fnmatch(glob, name, 0) == 0) {
528 if (perm != NULL &&
529 ((strcasecmp(perm, "allow") == 0) ||
530 (strcasecmp(perm, "yes") == 0)))
531 retval = TRUE;
532 else if (perm != NULL &&
533 ((strcasecmp(perm, "deny") == 0) ||
534 (strcasecmp(perm, "no") == 0)))
535 retval = FALSE;
536 else
537 retval = !def;
538 break;
539 }
540 }
541 (void) fclose(fd);
542 return (retval);
543 }
544
545 /*
546 * Check if user is allowed by /etc/ftpusers
547 * returns 0 for yes, 1 for no
548 */
549 int
550 checkaccess(name)
551 const char *name;
552 {
553
554 return (! checkuser(_PATH_FTPUSERS, name, TRUE, FALSE));
555 }
556
557 /*
558 * Terminate login as previous user, if any, resetting state;
559 * used when USER command is given or login fails.
560 */
561 static void
562 end_login()
563 {
564
565 (void) seteuid((uid_t)0);
566 if (logged_in)
567 logwtmp(ttyline, "", "");
568 pw = NULL;
569 logged_in = 0;
570 guest = 0;
571 dochroot = 0;
572 }
573
574 void
575 pass(passwd)
576 char *passwd;
577 {
578 int rval;
579 FILE *fd;
580 char const *cp, *shell, *home;
581
582 if (logged_in || askpasswd == 0) {
583 reply(503, "Login with USER first.");
584 return;
585 }
586 askpasswd = 0;
587 if (!guest) { /* "ftp" is only account allowed no password */
588 if (pw == NULL) {
589 rval = 1; /* failure below */
590 goto skip;
591 }
592 #ifdef KERBEROS
593 if (klogin(pw, "", hostname, passwd) == 0) {
594 rval = 0;
595 goto skip;
596 }
597 #endif
598 #ifdef KERBEROS5
599 if (klogin(pw, "", hostname, passwd) == 0) {
600 rval = 0;
601 goto skip;
602 }
603 #endif
604 #ifdef SKEY
605 if (skey_haskey(pw->pw_name) == 0 &&
606 skey_passcheck(pw->pw_name, passwd) != -1) {
607 rval = 0;
608 goto skip;
609 }
610 #endif
611 if (!sflag && *pw->pw_passwd != '\0' &&
612 !strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd)) {
613 rval = 0;
614 goto skip;
615 }
616 rval = 1;
617
618 skip:
619 if (pw != NULL && pw->pw_expire && time(NULL) >= pw->pw_expire)
620 rval = 2;
621 /*
622 * If rval > 0, the user failed the authentication check
623 * above. If rval == 0, either Kerberos or local authentication
624 * succeeded.
625 */
626 if (rval) {
627 reply(530, rval == 2 ? "Password expired." :
628 "Login incorrect.");
629 if (logging) {
630 syslog(LOG_NOTICE,
631 "FTP LOGIN FAILED FROM %s", remotehost);
632 syslog(LOG_AUTHPRIV | LOG_NOTICE,
633 "FTP LOGIN FAILED FROM %s, %s",
634 remotehost, curname);
635 }
636 pw = NULL;
637 if (login_attempts++ >= 5) {
638 syslog(LOG_NOTICE,
639 "repeated login failures from %s",
640 remotehost);
641 exit(0);
642 }
643 return;
644 }
645 }
646
647 /* password was ok; see if anything else prevents login */
648 if (checkaccess(pw->pw_name)) {
649 reply(530, "User %s may not use FTP.", pw->pw_name);
650 if (logging)
651 syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
652 remotehost, pw->pw_name);
653 pw = (struct passwd *) NULL;
654 return;
655 }
656 /* check for valid shell, if not guest user */
657 if ((shell = pw->pw_shell) == NULL || *shell == 0)
658 shell = _PATH_BSHELL;
659 while ((cp = getusershell()) != NULL)
660 if (strcmp(cp, shell) == 0)
661 break;
662 endusershell();
663 if (cp == NULL && guest == 0) {
664 reply(530, "User %s may not use FTP.", pw->pw_name);
665 if (logging)
666 syslog(LOG_NOTICE,
667 "FTP LOGIN REFUSED FROM %s, %s",
668 remotehost, pw->pw_name);
669 pw = (struct passwd *) NULL;
670 return;
671 }
672
673 login_attempts = 0; /* this time successful */
674 if (setegid((gid_t)pw->pw_gid) < 0) {
675 reply(550, "Can't set gid.");
676 return;
677 }
678 (void) initgroups(pw->pw_name, pw->pw_gid);
679
680 /* open wtmp before chroot */
681 logwtmp(ttyline, pw->pw_name, remotehost);
682 logged_in = 1;
683
684 dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name, FALSE, FALSE);
685
686 /* parse ftpd.conf, setting up various parameters */
687 if (guest)
688 parse_conf(CLASS_GUEST);
689 else if (dochroot)
690 parse_conf(CLASS_CHROOT);
691 else
692 parse_conf(CLASS_REAL);
693
694 home = "/";
695 if (guest) {
696 /*
697 * We MUST do a chdir() after the chroot. Otherwise
698 * the old current directory will be accessible as "."
699 * outside the new root!
700 */
701 if (chroot(anondir ? anondir : pw->pw_dir) < 0 ||
702 chdir("/") < 0) {
703 reply(550, "Can't set guest privileges.");
704 goto bad;
705 }
706 } else if (dochroot) {
707 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
708 reply(550, "Can't change root.");
709 goto bad;
710 }
711 } else if (chdir(pw->pw_dir) < 0) {
712 if (chdir("/") < 0) {
713 reply(530, "User %s: can't change directory to %s.",
714 pw->pw_name, pw->pw_dir);
715 goto bad;
716 } else
717 lreply(230, "No directory! Logging in with home=/");
718 } else
719 home = pw->pw_dir;
720 if (seteuid((uid_t)pw->pw_uid) < 0) {
721 reply(550, "Can't set uid.");
722 goto bad;
723 }
724 setenv("HOME", home, 1);
725
726
727 /*
728 * Display a login message, if it exists.
729 * N.B. reply(230,) must follow the message.
730 */
731 if ((fd = fopen(conffilename(_PATH_FTPLOGINMESG), "r")) != NULL) {
732 char *cp, line[LINE_MAX];
733
734 while (fgets(line, sizeof(line), fd) != NULL) {
735 if ((cp = strchr(line, '\n')) != NULL)
736 *cp = '\0';
737 lreply(230, "%s", line);
738 }
739 (void) fflush(stdout);
740 (void) fclose(fd);
741 }
742 show_chdir_messages(230);
743 if (guest) {
744 reply(230, "Guest login ok, access restrictions apply.");
745 #ifdef HASSETPROCTITLE
746 snprintf(proctitle, sizeof(proctitle),
747 "%s: anonymous/%.*s", remotehost,
748 (int) (sizeof(proctitle) - sizeof(remotehost) -
749 sizeof(": anonymous/")), passwd);
750 setproctitle(proctitle);
751 #endif /* HASSETPROCTITLE */
752 if (logging)
753 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
754 remotehost, passwd);
755 } else {
756 reply(230, "User %s logged in.", pw->pw_name);
757 #ifdef HASSETPROCTITLE
758 snprintf(proctitle, sizeof(proctitle),
759 "%s: %s", remotehost, pw->pw_name);
760 setproctitle(proctitle);
761 #endif /* HASSETPROCTITLE */
762 if (logging)
763 syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
764 remotehost, pw->pw_name);
765 }
766 (void) umask(curclass.umask);
767 return;
768 bad:
769 /* Forget all about it... */
770 end_login();
771 }
772
773 void
774 retrieve(cmd, name)
775 char *cmd, *name;
776 {
777 FILE *fin = NULL, *dout;
778 struct stat st;
779 int (*closefunc) __P((FILE *)) = NULL;
780 int log, sendrv, closerv, stderrfd, isconversion;
781
782 sendrv = closerv = stderrfd = -1;
783 isconversion = 0;
784 log = (cmd == 0);
785 if (cmd == NULL) {
786 fin = fopen(name, "r"), closefunc = fclose;
787 if (fin == NULL)
788 cmd = do_conversion(name);
789 if (cmd != NULL) {
790 isconversion++;
791 syslog(LOG_INFO, "get command: '%s'", cmd);
792 }
793 }
794 if (cmd != NULL) {
795 char line[BUFSIZ];
796 char temp[MAXPATHLEN];
797
798 (void)snprintf(temp, sizeof(temp), "%s", TMPFILE);
799 stderrfd = mkstemp(temp);
800 if (stderrfd != -1)
801 (void)unlink(temp);
802 (void)snprintf(line, sizeof(line), cmd, name), name = line;
803 fin = ftpd_popen(line, "r", stderrfd), closefunc = ftpd_pclose;
804 st.st_size = -1;
805 st.st_blksize = BUFSIZ;
806 }
807 if (fin == NULL) {
808 if (errno != 0) {
809 perror_reply(550, name);
810 if (log) {
811 LOGCMD("get", name);
812 }
813 }
814 if (stderrfd != -1)
815 (void)close(stderrfd);
816 return;
817 }
818 byte_count = -1;
819 if (cmd == NULL
820 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
821 reply(550, "%s: not a plain file.", name);
822 goto done;
823 }
824 if (restart_point) {
825 if (type == TYPE_A) {
826 off_t i;
827 int c;
828
829 for (i = 0; i < restart_point; i++) {
830 if ((c=getc(fin)) == EOF) {
831 perror_reply(550, name);
832 goto done;
833 }
834 if (c == '\n')
835 i++;
836 }
837 } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
838 perror_reply(550, name);
839 goto done;
840 }
841 }
842 dout = dataconn(name, st.st_size, "w");
843 if (dout == NULL)
844 goto done;
845 sendrv = send_data(fin, dout, st.st_blksize);
846 (void) fclose(dout);
847 data = -1;
848 pdata = -1;
849 done:
850 if (log)
851 LOGBYTES("get", name, byte_count);
852 closerv = (*closefunc)(fin);
853 if (sendrv == 0) {
854 FILE *err;
855 struct stat sb;
856
857 if (cmd != NULL && closerv != 0) {
858 lreply(226,
859 "Command returned an exit status of %d",
860 closerv);
861 if (isconversion)
862 syslog(LOG_INFO,
863 "get command: '%s' returned %d",
864 cmd, closerv);
865 }
866 if (cmd != NULL && stderrfd != -1 &&
867 (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 &&
868 ((err = fdopen(stderrfd, "r")) != NULL)) {
869 char *cp, line[LINE_MAX];
870
871 lreply(226, "Command error messages:");
872 rewind(err);
873 while (fgets(line, sizeof(line), err) != NULL) {
874 if ((cp = strchr(line, '\n')) != NULL)
875 *cp = '\0';
876 lreply(226, " %s", line);
877 }
878 (void) fflush(stdout);
879 (void) fclose(err);
880 lreply(226, "End of command error messages.");
881 /* a reply(226,) must follow */
882 }
883 reply(226, "Transfer complete.");
884 }
885 if (stderrfd != -1)
886 (void)close(stderrfd);
887 }
888
889 void
890 store(name, mode, unique)
891 char *name, *mode;
892 int unique;
893 {
894 FILE *fout, *din;
895 struct stat st;
896 int (*closefunc) __P((FILE *));
897
898 if (unique && stat(name, &st) == 0 &&
899 (name = gunique(name)) == NULL) {
900 LOGCMD(*mode == 'w' ? "put" : "append", name);
901 return;
902 }
903
904 if (restart_point)
905 mode = "r+";
906 fout = fopen(name, mode);
907 closefunc = fclose;
908 if (fout == NULL) {
909 perror_reply(553, name);
910 LOGCMD(*mode == 'w' ? "put" : "append", name);
911 return;
912 }
913 byte_count = -1;
914 if (restart_point) {
915 if (type == TYPE_A) {
916 off_t i;
917 int c;
918
919 for (i = 0; i < restart_point; i++) {
920 if ((c=getc(fout)) == EOF) {
921 perror_reply(550, name);
922 goto done;
923 }
924 if (c == '\n')
925 i++;
926 }
927 /*
928 * We must do this seek to "current" position
929 * because we are changing from reading to
930 * writing.
931 */
932 if (fseek(fout, 0L, SEEK_CUR) < 0) {
933 perror_reply(550, name);
934 goto done;
935 }
936 } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
937 perror_reply(550, name);
938 goto done;
939 }
940 }
941 din = dataconn(name, (off_t)-1, "r");
942 if (din == NULL)
943 goto done;
944 if (receive_data(din, fout) == 0) {
945 if (unique)
946 reply(226, "Transfer complete (unique file name:%s).",
947 name);
948 else
949 reply(226, "Transfer complete.");
950 }
951 (void) fclose(din);
952 data = -1;
953 pdata = -1;
954 done:
955 LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
956 (*closefunc)(fout);
957 }
958
959 static FILE *
960 getdatasock(mode)
961 char *mode;
962 {
963 int on = 1, s, t, tries;
964
965 if (data >= 0)
966 return (fdopen(data, mode));
967 (void) seteuid((uid_t)0);
968 s = socket(AF_INET, SOCK_STREAM, 0);
969 if (s < 0)
970 goto bad;
971 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
972 (char *) &on, sizeof(on)) < 0)
973 goto bad;
974 /* anchor socket to avoid multi-homing problems */
975 data_source.sin_len = sizeof(struct sockaddr_in);
976 data_source.sin_family = AF_INET;
977 data_source.sin_addr = ctrl_addr.sin_addr;
978 for (tries = 1; ; tries++) {
979 if (bind(s, (struct sockaddr *)&data_source,
980 sizeof(data_source)) >= 0)
981 break;
982 if (errno != EADDRINUSE || tries > 10)
983 goto bad;
984 sleep(tries);
985 }
986 (void) seteuid((uid_t)pw->pw_uid);
987 #ifdef IP_TOS
988 on = IPTOS_THROUGHPUT;
989 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
990 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
991 #endif
992 return (fdopen(s, mode));
993 bad:
994 /* Return the real value of errno (close may change it) */
995 t = errno;
996 (void) seteuid((uid_t)pw->pw_uid);
997 (void) close(s);
998 errno = t;
999 return (NULL);
1000 }
1001
1002 static FILE *
1003 dataconn(name, size, mode)
1004 char *name;
1005 off_t size;
1006 char *mode;
1007 {
1008 char sizebuf[32];
1009 FILE *file;
1010 int retry = 0, tos;
1011
1012 file_size = size;
1013 byte_count = 0;
1014 if (size != (off_t) -1)
1015 (void)snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)",
1016 (long long)size);
1017 else
1018 sizebuf[0] = '\0';
1019 if (pdata >= 0) {
1020 struct sockaddr_in from;
1021 int s, fromlen = sizeof(from);
1022
1023 (void) alarm(curclass.timeout);
1024 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
1025 (void) alarm(0);
1026 if (s < 0) {
1027 reply(425, "Can't open data connection.");
1028 (void) close(pdata);
1029 pdata = -1;
1030 return (NULL);
1031 }
1032 (void) close(pdata);
1033 pdata = s;
1034 #ifdef IP_TOS
1035 tos = IPTOS_THROUGHPUT;
1036 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
1037 sizeof(int));
1038 #endif
1039 reply(150, "Opening %s mode data connection for '%s'%s.",
1040 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1041 return (fdopen(pdata, mode));
1042 }
1043 if (data >= 0) {
1044 reply(125, "Using existing data connection for '%s'%s.",
1045 name, sizebuf);
1046 usedefault = 1;
1047 return (fdopen(data, mode));
1048 }
1049 if (usedefault)
1050 data_dest = his_addr;
1051 usedefault = 1;
1052 file = getdatasock(mode);
1053 if (file == NULL) {
1054 reply(425, "Can't create data socket (%s,%d): %s.",
1055 inet_ntoa(data_source.sin_addr),
1056 ntohs(data_source.sin_port), strerror(errno));
1057 return (NULL);
1058 }
1059 data = fileno(file);
1060 while (connect(data, (struct sockaddr *)&data_dest,
1061 sizeof(data_dest)) < 0) {
1062 if (errno == EADDRINUSE && retry < swaitmax) {
1063 sleep((unsigned) swaitint);
1064 retry += swaitint;
1065 continue;
1066 }
1067 perror_reply(425, "Can't build data connection");
1068 (void) fclose(file);
1069 data = -1;
1070 return (NULL);
1071 }
1072 reply(150, "Opening %s mode data connection for '%s'%s.",
1073 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1074 return (file);
1075 }
1076
1077 /*
1078 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1079 * encapsulation of the data subject * to Mode, Structure, and Type.
1080 *
1081 * NB: Form isn't handled.
1082 */
1083 static int
1084 send_data(instr, outstr, blksize)
1085 FILE *instr, *outstr;
1086 off_t blksize;
1087 {
1088 int c, cnt, filefd, netfd;
1089 char *buf;
1090
1091 transflag++;
1092 if (setjmp(urgcatch)) {
1093 transflag = 0;
1094 return (-1);
1095 }
1096
1097 switch (type) {
1098
1099 case TYPE_A:
1100 while ((c = getc(instr)) != EOF) {
1101 byte_count++;
1102 if (c == '\n') {
1103 if (ferror(outstr))
1104 goto data_err;
1105 (void) putc('\r', outstr);
1106 }
1107 (void) putc(c, outstr);
1108 }
1109 fflush(outstr);
1110 transflag = 0;
1111 if (ferror(instr))
1112 goto file_err;
1113 if (ferror(outstr))
1114 goto data_err;
1115 return (0);
1116
1117 case TYPE_I:
1118 case TYPE_L:
1119 if ((buf = malloc((u_int)blksize)) == NULL) {
1120 transflag = 0;
1121 perror_reply(451, "Local resource failure: malloc");
1122 return (-1);
1123 }
1124 netfd = fileno(outstr);
1125 filefd = fileno(instr);
1126 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1127 write(netfd, buf, cnt) == cnt)
1128 byte_count += cnt;
1129 transflag = 0;
1130 (void)free(buf);
1131 if (cnt != 0) {
1132 if (cnt < 0)
1133 goto file_err;
1134 goto data_err;
1135 }
1136 return (0);
1137 default:
1138 transflag = 0;
1139 reply(550, "Unimplemented TYPE %d in send_data", type);
1140 return (-1);
1141 }
1142
1143 data_err:
1144 transflag = 0;
1145 perror_reply(426, "Data connection");
1146 return (-1);
1147
1148 file_err:
1149 transflag = 0;
1150 perror_reply(551, "Error on input file");
1151 return (-1);
1152 }
1153
1154 /*
1155 * Transfer data from peer to "outstr" using the appropriate encapulation of
1156 * the data subject to Mode, Structure, and Type.
1157 *
1158 * N.B.: Form isn't handled.
1159 */
1160 static int
1161 receive_data(instr, outstr)
1162 FILE *instr, *outstr;
1163 {
1164 int c, cnt, bare_lfs;
1165 char buf[BUFSIZ];
1166 #ifdef __GNUC__
1167 (void) &bare_lfs;
1168 #endif
1169
1170 bare_lfs = 0;
1171 transflag++;
1172 if (setjmp(urgcatch)) {
1173 transflag = 0;
1174 return (-1);
1175 }
1176
1177 switch (type) {
1178
1179 case TYPE_I:
1180 case TYPE_L:
1181 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1182 if (write(fileno(outstr), buf, cnt) != cnt)
1183 goto file_err;
1184 byte_count += cnt;
1185 }
1186 if (cnt < 0)
1187 goto data_err;
1188 transflag = 0;
1189 return (0);
1190
1191 case TYPE_E:
1192 reply(553, "TYPE E not implemented.");
1193 transflag = 0;
1194 return (-1);
1195
1196 case TYPE_A:
1197 while ((c = getc(instr)) != EOF) {
1198 byte_count++;
1199 if (c == '\n')
1200 bare_lfs++;
1201 while (c == '\r') {
1202 if (ferror(outstr))
1203 goto data_err;
1204 if ((c = getc(instr)) != '\n') {
1205 (void) putc ('\r', outstr);
1206 if (c == '\0' || c == EOF)
1207 goto contin2;
1208 }
1209 }
1210 (void) putc(c, outstr);
1211 contin2: ;
1212 }
1213 fflush(outstr);
1214 if (ferror(instr))
1215 goto data_err;
1216 if (ferror(outstr))
1217 goto file_err;
1218 transflag = 0;
1219 if (bare_lfs) {
1220 lreply(226,
1221 "WARNING! %d bare linefeeds received in ASCII mode",
1222 bare_lfs);
1223 (void)printf(" File may not have transferred correctly.\r\n");
1224 }
1225 return (0);
1226 default:
1227 reply(550, "Unimplemented TYPE %d in receive_data", type);
1228 transflag = 0;
1229 return (-1);
1230 }
1231
1232 data_err:
1233 transflag = 0;
1234 perror_reply(426, "Data Connection");
1235 return (-1);
1236
1237 file_err:
1238 transflag = 0;
1239 perror_reply(452, "Error writing file");
1240 return (-1);
1241 }
1242
1243 void
1244 statfilecmd(filename)
1245 char *filename;
1246 {
1247 FILE *fin;
1248 int c;
1249 char line[LINE_MAX];
1250
1251 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1252 fin = ftpd_popen(line, "r", STDOUT_FILENO);
1253 lreply(211, "status of %s:", filename);
1254 while ((c = getc(fin)) != EOF) {
1255 if (c == '\n') {
1256 if (ferror(stdout)){
1257 perror_reply(421, "control connection");
1258 (void) ftpd_pclose(fin);
1259 dologout(1);
1260 /* NOTREACHED */
1261 }
1262 if (ferror(fin)) {
1263 perror_reply(551, filename);
1264 (void) ftpd_pclose(fin);
1265 return;
1266 }
1267 (void) putc('\r', stdout);
1268 }
1269 (void) putc(c, stdout);
1270 }
1271 (void) ftpd_pclose(fin);
1272 reply(211, "End of Status");
1273 }
1274
1275 void
1276 statcmd()
1277 {
1278 struct sockaddr_in *sin;
1279 u_char *a, *p;
1280
1281 lreply(211, "%s FTP server status:", hostname);
1282 lreply(211, "%s", version);
1283 if (isdigit(remotehost[0]))
1284 lreply(211, "Connected to %s", remotehost);
1285 else
1286 lreply(211, "Connected to %s (%s)", remotehost,
1287 inet_ntoa(his_addr.sin_addr));
1288 if (logged_in) {
1289 if (guest)
1290 lreply(211, "Logged in anonymously");
1291 else
1292 lreply(211, "Logged in as %s", pw->pw_name);
1293 } else if (askpasswd)
1294 lreply(211, "Waiting for password");
1295 else
1296 lreply(211, "Waiting for user name");
1297 printf("211- TYPE: %s", typenames[type]);
1298 if (type == TYPE_A || type == TYPE_E)
1299 printf(", FORM: %s", formnames[form]);
1300 if (type == TYPE_L)
1301 #if NBBY == 8
1302 printf(" %d", NBBY);
1303 #else
1304 printf(" %d", bytesize); /* need definition! */
1305 #endif
1306 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1307 strunames[stru], modenames[mode]);
1308 if (data != -1)
1309 lreply(211, "Data connection open");
1310 else if (pdata != -1) {
1311 printf("211- in Passive mode");
1312 sin = &pasv_addr;
1313 goto printaddr;
1314 } else if (usedefault == 0) {
1315 printf("211- PORT");
1316 sin = &data_dest;
1317 printaddr:
1318 a = (u_char *) &sin->sin_addr;
1319 p = (u_char *) &sin->sin_port;
1320 #define UC(b) (((int) b) & 0xff)
1321 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1322 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1323 #undef UC
1324 } else
1325 lreply(211, "No data connection");
1326
1327 if (logged_in) {
1328 struct ftpconv *cp;
1329
1330 lreply(211, "");
1331 lreply(211, "Class: %s", curclass.classname);
1332 lreply(211, "Check PORT commands: %sabled",
1333 curclass.checkportcmd ? "en" : "dis");
1334 if (curclass.display)
1335 lreply(211, "Display file: %s", curclass.display);
1336 if (curclass.notify)
1337 lreply(211, "Notify fileglob: %s", curclass.notify);
1338 lreply(211, "Idle timeout: %d, maximum timeout: %d",
1339 curclass.timeout, curclass.maxtimeout);
1340 lreply(211, "DELE, MKD, RMD, UMASK, CHMOD commands: %sabled",
1341 curclass.modify ? "en" : "dis");
1342 lreply(211, "Umask: %.04o", curclass.umask);
1343 for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
1344 if (cp->suffix == NULL || cp->types == NULL ||
1345 cp->command == NULL)
1346 continue;
1347 lreply(211,
1348 "Conversion: %s [%s] disable: %s, command: %s",
1349 cp->suffix, cp->types, cp->disable, cp->command);
1350 }
1351 }
1352
1353 reply(211, "End of status");
1354 }
1355
1356 void
1357 fatal(s)
1358 char *s;
1359 {
1360
1361 reply(451, "Error in server: %s\n", s);
1362 reply(221, "Closing connection due to server error.");
1363 dologout(0);
1364 /* NOTREACHED */
1365 }
1366
1367 void
1368 #ifdef __STDC__
1369 reply(int n, const char *fmt, ...)
1370 #else
1371 reply(n, fmt, va_alist)
1372 int n;
1373 char *fmt;
1374 va_dcl
1375 #endif
1376 {
1377 va_list ap;
1378 #ifdef __STDC__
1379 va_start(ap, fmt);
1380 #else
1381 va_start(ap);
1382 #endif
1383 (void)printf("%d ", n);
1384 (void)vprintf(fmt, ap);
1385 (void)printf("\r\n");
1386 (void)fflush(stdout);
1387 if (debug) {
1388 syslog(LOG_DEBUG, "<--- %d ", n);
1389 vsyslog(LOG_DEBUG, fmt, ap);
1390 }
1391 }
1392
1393 void
1394 #ifdef __STDC__
1395 lreply(int n, const char *fmt, ...)
1396 #else
1397 lreply(n, fmt, va_alist)
1398 int n;
1399 char *fmt;
1400 va_dcl
1401 #endif
1402 {
1403 va_list ap;
1404 #ifdef __STDC__
1405 va_start(ap, fmt);
1406 #else
1407 va_start(ap);
1408 #endif
1409 (void)printf("%d- ", n);
1410 (void)vprintf(fmt, ap);
1411 (void)printf("\r\n");
1412 (void)fflush(stdout);
1413 if (debug) {
1414 syslog(LOG_DEBUG, "<--- %d- ", n);
1415 vsyslog(LOG_DEBUG, fmt, ap);
1416 }
1417 }
1418
1419 static void
1420 ack(s)
1421 char *s;
1422 {
1423
1424 reply(250, "%s command successful.", s);
1425 }
1426
1427 void
1428 nack(s)
1429 char *s;
1430 {
1431
1432 reply(502, "%s command not implemented.", s);
1433 }
1434
1435 void
1436 delete(name)
1437 char *name;
1438 {
1439
1440 LOGCMD("delete", name);
1441 if (remove(name) < 0)
1442 perror_reply(550, name);
1443 else
1444 ack("DELE");
1445 }
1446
1447 void
1448 cwd(path)
1449 const char *path;
1450 {
1451
1452 if (chdir(path) < 0)
1453 perror_reply(550, path);
1454 else {
1455 show_chdir_messages(250);
1456 ack("CWD");
1457 }
1458 }
1459
1460 static void
1461 replydirname(name, message)
1462 const char *name, *message;
1463 {
1464 char npath[MAXPATHLEN + 1];
1465 int i;
1466
1467 for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
1468 npath[i] = *name;
1469 if (*name == '"')
1470 npath[++i] = '"';
1471 }
1472 npath[i] = '\0';
1473 reply(257, "\"%s\" %s", npath, message);
1474 }
1475
1476 void
1477 makedir(name)
1478 char *name;
1479 {
1480
1481 LOGCMD("mkdir", name);
1482 if (mkdir(name, 0777) < 0)
1483 perror_reply(550, name);
1484 else
1485 replydirname(name, "directory created.");
1486 }
1487
1488 void
1489 removedir(name)
1490 char *name;
1491 {
1492
1493 LOGCMD("rmdir", name);
1494 if (rmdir(name) < 0)
1495 perror_reply(550, name);
1496 else
1497 ack("RMD");
1498 }
1499
1500 void
1501 pwd()
1502 {
1503 char path[MAXPATHLEN + 1];
1504
1505 if (getcwd(path, sizeof(path) - 1) == NULL)
1506 reply(550, "Can't get the current directory: %s.",
1507 strerror(errno));
1508 else
1509 replydirname(path, "is the current directory.");
1510 }
1511
1512 char *
1513 renamefrom(name)
1514 char *name;
1515 {
1516 struct stat st;
1517
1518 if (stat(name, &st) < 0) {
1519 perror_reply(550, name);
1520 return (NULL);
1521 }
1522 reply(350, "File exists, ready for destination name");
1523 return (name);
1524 }
1525
1526 void
1527 renamecmd(from, to)
1528 char *from, *to;
1529 {
1530
1531 LOGCMD2("rename", from, to);
1532 if (rename(from, to) < 0)
1533 perror_reply(550, "rename");
1534 else
1535 ack("RNTO");
1536 }
1537
1538 static void
1539 dolog(sin)
1540 struct sockaddr_in *sin;
1541 {
1542 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1543 sizeof(struct in_addr), AF_INET);
1544
1545 if (hp)
1546 (void)strncpy(remotehost, hp->h_name, sizeof(remotehost));
1547 else
1548 (void)strncpy(remotehost, inet_ntoa(sin->sin_addr),
1549 sizeof(remotehost));
1550 remotehost[sizeof(remotehost) - 1] = '\0';
1551 #ifdef HASSETPROCTITLE
1552 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1553 setproctitle(proctitle);
1554 #endif /* HASSETPROCTITLE */
1555
1556 if (logging)
1557 syslog(LOG_INFO, "connection from %s", remotehost);
1558 }
1559
1560 /*
1561 * Record logout in wtmp file
1562 * and exit with supplied status.
1563 */
1564 void
1565 dologout(status)
1566 int status;
1567 {
1568 /*
1569 * Prevent reception of SIGURG from resulting in a resumption
1570 * back to the main program loop.
1571 */
1572 transflag = 0;
1573
1574 if (logged_in) {
1575 (void) seteuid((uid_t)0);
1576 logwtmp(ttyline, "", "");
1577 #ifdef KERBEROS
1578 if (!notickets && krbtkfile_env)
1579 unlink(krbtkfile_env);
1580 #endif
1581 }
1582 /* beware of flushing buffers after a SIGPIPE */
1583 _exit(status);
1584 }
1585
1586 static void
1587 myoob(signo)
1588 int signo;
1589 {
1590 char *cp;
1591
1592 /* only process if transfer occurring */
1593 if (!transflag)
1594 return;
1595 cp = tmpline;
1596 if (getline(cp, 7, stdin) == NULL) {
1597 reply(221, "You could at least say goodbye.");
1598 dologout(0);
1599 }
1600 if (strcasecmp(cp, "ABOR\r\n") == 0) {
1601 tmpline[0] = '\0';
1602 reply(426, "Transfer aborted. Data connection closed.");
1603 reply(226, "Abort successful");
1604 longjmp(urgcatch, 1);
1605 }
1606 if (strcasecmp(cp, "STAT\r\n") == 0) {
1607 if (file_size != (off_t) -1)
1608 reply(213, "Status: %qd of %qd bytes transferred",
1609 byte_count, file_size);
1610 else
1611 reply(213, "Status: %qd bytes transferred", byte_count);
1612 }
1613 }
1614
1615 /*
1616 * Note: a response of 425 is not mentioned as a possible response to
1617 * the PASV command in RFC959. However, it has been blessed as
1618 * a legitimate response by Jon Postel in a telephone conversation
1619 * with Rick Adams on 25 Jan 89.
1620 */
1621 void
1622 passive()
1623 {
1624 int len;
1625 char *p, *a;
1626
1627 pdata = socket(AF_INET, SOCK_STREAM, 0);
1628 if (pdata < 0 || !logged_in) {
1629 perror_reply(425, "Can't open passive connection");
1630 return;
1631 }
1632 pasv_addr = ctrl_addr;
1633 pasv_addr.sin_port = 0;
1634 (void) seteuid((uid_t)0);
1635 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1636 (void) seteuid((uid_t)pw->pw_uid);
1637 goto pasv_error;
1638 }
1639 (void) seteuid((uid_t)pw->pw_uid);
1640 len = sizeof(pasv_addr);
1641 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1642 goto pasv_error;
1643 if (listen(pdata, 1) < 0)
1644 goto pasv_error;
1645 a = (char *) &pasv_addr.sin_addr;
1646 p = (char *) &pasv_addr.sin_port;
1647
1648 #define UC(b) (((int) b) & 0xff)
1649
1650 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1651 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1652 return;
1653
1654 pasv_error:
1655 (void) close(pdata);
1656 pdata = -1;
1657 perror_reply(425, "Can't open passive connection");
1658 return;
1659 }
1660
1661 /*
1662 * Generate unique name for file with basename "local".
1663 * The file named "local" is already known to exist.
1664 * Generates failure reply on error.
1665 *
1666 * XXX this function should under go changes similar to
1667 * the mktemp(3)/mkstemp(3) changes.
1668 */
1669 static char *
1670 gunique(local)
1671 char *local;
1672 {
1673 static char new[MAXPATHLEN];
1674 struct stat st;
1675 int count, len;
1676 char *cp;
1677
1678 cp = strrchr(local, '/');
1679 if (cp)
1680 *cp = '\0';
1681 if (stat(cp ? local : ".", &st) < 0) {
1682 perror_reply(553, cp ? local : ".");
1683 return (NULL);
1684 }
1685 if (cp)
1686 *cp = '/';
1687 (void) strcpy(new, local);
1688 len = strlen(new);
1689 cp = new + len;
1690 *cp++ = '.';
1691 for (count = 1; count < 100; count++) {
1692 (void)snprintf(cp, sizeof(new) - len - 2, "%d", count);
1693 if (stat(new, &st) < 0)
1694 return (new);
1695 }
1696 reply(452, "Unique file name cannot be created.");
1697 return (NULL);
1698 }
1699
1700 /*
1701 * Format and send reply containing system error number.
1702 */
1703 void
1704 perror_reply(code, string)
1705 int code;
1706 const char *string;
1707 {
1708
1709 reply(code, "%s: %s.", string, strerror(errno));
1710 }
1711
1712 static char *onefile[] = {
1713 "",
1714 0
1715 };
1716
1717 void
1718 send_file_list(whichf)
1719 char *whichf;
1720 {
1721 struct stat st;
1722 DIR *dirp = NULL;
1723 struct dirent *dir;
1724 FILE *dout = NULL;
1725 char **dirlist, *dirname;
1726 int simple = 0;
1727 int freeglob = 0;
1728 glob_t gl;
1729 #ifdef __GNUC__
1730 (void) &dout;
1731 (void) &dirlist;
1732 (void) &simple;
1733 (void) &freeglob;
1734 #endif
1735
1736 if (strpbrk(whichf, "~{[*?") != NULL) {
1737 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
1738
1739 memset(&gl, 0, sizeof(gl));
1740 freeglob = 1;
1741 if (glob(whichf, flags, 0, &gl)) {
1742 reply(550, "not found");
1743 goto out;
1744 } else if (gl.gl_pathc == 0) {
1745 errno = ENOENT;
1746 perror_reply(550, whichf);
1747 goto out;
1748 }
1749 dirlist = gl.gl_pathv;
1750 } else {
1751 onefile[0] = whichf;
1752 dirlist = onefile;
1753 simple = 1;
1754 }
1755
1756 if (setjmp(urgcatch)) {
1757 transflag = 0;
1758 goto out;
1759 }
1760 while ((dirname = *dirlist++) != NULL) {
1761 int trailingslash = 0;
1762
1763 if (stat(dirname, &st) < 0) {
1764 /*
1765 * If user typed "ls -l", etc, and the client
1766 * used NLST, do what the user meant.
1767 */
1768 if (dirname[0] == '-' && *dirlist == NULL &&
1769 transflag == 0) {
1770 retrieve("/bin/ls %s", dirname);
1771 goto out;
1772 }
1773 perror_reply(550, whichf);
1774 if (dout != NULL) {
1775 (void) fclose(dout);
1776 transflag = 0;
1777 data = -1;
1778 pdata = -1;
1779 }
1780 goto out;
1781 }
1782
1783 if (S_ISREG(st.st_mode)) {
1784 if (dout == NULL) {
1785 dout = dataconn("file list", (off_t)-1, "w");
1786 if (dout == NULL)
1787 goto out;
1788 transflag++;
1789 }
1790 fprintf(dout, "%s%s\n", dirname,
1791 type == TYPE_A ? "\r" : "");
1792 byte_count += strlen(dirname) + 1;
1793 continue;
1794 } else if (!S_ISDIR(st.st_mode))
1795 continue;
1796
1797 if (dirname[strlen(dirname) - 1] == '/')
1798 trailingslash++;
1799
1800 if ((dirp = opendir(dirname)) == NULL)
1801 continue;
1802
1803 while ((dir = readdir(dirp)) != NULL) {
1804 char nbuf[MAXPATHLEN];
1805
1806 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1807 continue;
1808 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1809 dir->d_namlen == 2)
1810 continue;
1811
1812 (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
1813 trailingslash ? "" : "/", dir->d_name);
1814
1815 /*
1816 * We have to do a stat to ensure it's
1817 * not a directory or special file.
1818 */
1819 if (simple || (stat(nbuf, &st) == 0 &&
1820 S_ISREG(st.st_mode))) {
1821 if (dout == NULL) {
1822 dout = dataconn("file list", (off_t)-1,
1823 "w");
1824 if (dout == NULL)
1825 goto out;
1826 transflag++;
1827 }
1828 if (nbuf[0] == '.' && nbuf[1] == '/')
1829 fprintf(dout, "%s%s\n", &nbuf[2],
1830 type == TYPE_A ? "\r" : "");
1831 else
1832 fprintf(dout, "%s%s\n", nbuf,
1833 type == TYPE_A ? "\r" : "");
1834 byte_count += strlen(nbuf) + 1;
1835 }
1836 }
1837 (void) closedir(dirp);
1838 }
1839
1840 if (dout == NULL)
1841 reply(550, "No files found.");
1842 else if (ferror(dout) != 0)
1843 perror_reply(550, "Data connection");
1844 else
1845 reply(226, "Transfer complete.");
1846
1847 transflag = 0;
1848 if (dout != NULL)
1849 (void) fclose(dout);
1850 data = -1;
1851 pdata = -1;
1852 out:
1853 if (freeglob) {
1854 freeglob = 0;
1855 globfree(&gl);
1856 }
1857 }
1858
1859 char *
1860 conffilename(s)
1861 const char *s;
1862 {
1863 static char filename[MAXPATHLEN];
1864
1865 (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
1866 return filename;
1867 }
1868
1869 char *
1870 xstrdup(s)
1871 const char *s;
1872 {
1873 char *new = strdup(s);
1874
1875 if (new == NULL)
1876 fatal("Local resource failure: malloc");
1877 /* NOTREACHED */
1878 return (new);
1879 }
1880