1 /* opieftpd.c: Main program for an FTP daemon.
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
17 Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18 opiestrncpy(). Fix incorrect use of setproctitle().
19 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20 done already (and conditionally) in opie_cfg.h.
21 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22 Merged in a security fix to BSD-derived ftpds.
23 Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24 Moved LS_COMMAND here.
25 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26 Removed useless strings (I don't think that removing the
27 ucb copyright one is a problem -- please let me know if
28 I'm wrong). Changed default CMASK to 077. Removed random
29 comments. Use ANSI stdargs for reply/lreply if we can,
30 added stdargs version of reply/lreply. Don't declare the
31 tos variable unless IP_TOS defined. Include stdargs headers
32 early. More headers ifdefed. Made everything static.
33 Got rid of gethostname() call and use of hostname. Pared
34 down status response for places where header files frequently
35 cause trouble. Made logging of user logins (ala -l)
36 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38 result of opiechallenge (allows access control to work).
39 Added patches for AIX. Symbol changes for autoconf.
40 Modified at NRL for OPIE 2.01. Changed password lookup handling
41 to avoid problems with drain-bamaged shadow password packages.
42 Properly handle internal state for anonymous FTP. Unlock
43 user accounts properly if login fails because of /etc/shells.
44 Make sure to close syslog by function to avoid problems with
45 drain bamaged syslog implementations.
46 Modified at NRL for OPIE 2.0.
47 Originally from BSD Net/2.
49 There is some really, really ugly code in here.
51 $FreeBSD: src/contrib/opie/opieftpd.c,v 1.2.6.5 2002/07/15 14:48:43 des Exp $
52 $DragonFly: src/contrib/opie/opieftpd.c,v 1.2 2003/06/17 04:24:05 dillon Exp $
55 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
56 * All rights reserved.
58 * Redistribution and use in source and binary forms, with or without
59 * modification, are permitted provided that the following conditions
61 * 1. Redistributions of source code must retain the above copyright
62 * notice, this list of conditions and the following disclaimer.
63 * 2. Redistributions in binary form must reproduce the above copyright
64 * notice, this list of conditions and the following disclaimer in the
65 * documentation and/or other materials provided with the distribution.
66 * 3. All advertising materials mentioning features or use of this software
67 * must display the following acknowledgement:
68 * This product includes software developed by the University of
69 * California, Berkeley and its contributors.
70 * 4. Neither the name of the University nor the names of its contributors
71 * may be used to endorse or promote products derived from this software
72 * without specific prior written permission.
74 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
75 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
76 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
77 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
78 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
79 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
80 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
81 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
82 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
83 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
91 #endif /* HAVE_ANSISTDARG */
98 #include <sys/param.h>
99 #endif /* HAVE_SYS_PARAM_H */
100 #include <sys/stat.h>
101 /* #include <sys/ioctl.h> */
102 #include <sys/socket.h>
103 #include <sys/wait.h>
105 #include <sys/fcntl.h>
108 #endif /* SYS_FCNTL_H */
109 #include <sys/types.h>
111 #include <netinet/in.h>
112 #include <netinet/in_systm.h>
113 #include <netinet/ip.h>
116 #include <arpa/ftp.h>
117 #include <arpa/inet.h>
118 #include <arpa/telnet.h>
124 #endif /* HAVE_TIME_H */
127 #endif /* HAVE_PWD_H */
134 #endif /* HAVE_UNISTD_H */
145 #endif /* HAVE_SHADOW_H */
149 #endif /* HAVE_CRYPT_H */
151 #if HAVE_SYS_UTSNAME_H
152 #include <sys/utsname.h>
153 #endif /* HAVE_SYS_UTSNAME_H */
157 #include <sys/priv.h>
161 #ifndef IPTOS_THROUGHPUT
163 #endif /* !IPTOS_THROUGHPUT */
164 #ifndef IPTOS_LOWDELAY
166 #endif /* !IPTOS_LOWDELAY */
170 extern char *home
; /* pointer to home directory for glob */
171 extern FILE *ftpd_popen
__P((char *, char *));
172 extern int ftpd_pclose
__P((FILE *));
174 extern off_t restart_point
;
176 static struct sockaddr_in ctrl_addr
;
177 static struct sockaddr_in data_source
;
178 struct sockaddr_in data_dest
;
179 struct sockaddr_in his_addr
;
180 static struct sockaddr_in pasv_addr
;
184 static jmp_buf urgcatch
;
188 int timeout
= 900; /* timeout after 15 minutes of inactivity */
189 int maxtimeout
= 7200; /* don't allow idle time to be set beyond 2 hours */
193 #endif /* DOANONYMOUS */
196 static int stru
; /* avoid C keyword */
198 int usedefault
= 1; /* for data transfers */
199 int pdata
= -1; /* for passive mode */
200 static int transflag
;
201 static off_t file_size
;
202 static off_t byte_count
;
204 #if (!defined(CMASK) || CMASK == 0)
209 static int defumask
= CMASK
; /* default umask value */
211 char remotehost
[MAXHOSTNAMELEN
];
214 * Timeout intervals for retrying connections
215 * to hosts that don't accept PORT cmds. This
216 * is a kludge, but given the problems with TCP...
218 #define SWAITMAX 90 /* wait at most 90 seconds */
219 #define SWAITINT 5 /* interval between retries */
221 static int swaitmax
= SWAITMAX
;
222 static int swaitint
= SWAITINT
;
225 static char **Argv
= NULL
; /* pointer to argument vector */
226 static char *LastArgv
= NULL
; /* end of argv */
227 static char proctitle
[BUFSIZ
]; /* initial part of title */
230 static int af_pwok
= 0, pwok
= 0;
231 static struct opie opiestate
;
233 VOIDRET perror_reply
__P((int, char *));
234 VOIDRET dologout
__P((int));
235 char *getline
__P((char *, int, FILE *));
236 VOIDRET upper
__P((char *));
238 static VOIDRET lostconn
__P((int));
239 static VOIDRET myoob
__P((int));
240 static FILE *getdatasock
__P((char *));
241 static FILE *dataconn
__P((char *, off_t
, char *));
242 static int checkuser
__P((char *));
243 static VOIDRET end_login
__P((void));
244 static VOIDRET send_data
__P((FILE *, FILE *, off_t
));
245 static int receive_data
__P((FILE *, FILE *));
246 static char *gunique
__P((char *));
247 static char *sgetsave
__P((char *));
249 int opielogwtmp
__P((char *, char *, char *, char *));
251 int fclose
__P((FILE *));
253 #ifdef HAVE_ANSISTDARG
254 VOIDRET reply
FUNCTION((stdarg is ANSI only
), int n AND
char *fmt AND
...)
260 vsprintf(buffer
, fmt
, ap
);
263 printf("%d %s\r\n", n
, buffer
);
266 syslog(LOG_DEBUG
, "<--- %d %s", n
, buffer
);
268 #else /* HAVE_ANSISTDARG */
269 VOIDRET reply
FUNCTION((n
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
), int n AND
char *fmt AND
int p0 AND
int p1 AND
int p2 AND
int p3 AND
int p4 AND
int p5
)
272 printf(fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
276 syslog(LOG_DEBUG
, "<--- %d ", n
);
277 syslog(LOG_DEBUG
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
280 #endif /* HAVE_ANSISTDARG */
282 #ifdef HAVE_ANSISTDARG
283 VOIDRET lreply
FUNCTION((stdarg is ANSI only
), int n AND
char *fmt AND
...)
289 vsprintf(buffer
, fmt
, ap
);
292 printf("%d- %s\r\n", n
, buffer
);
295 syslog(LOG_DEBUG
, "<--- %d- %s", n
, buffer
);
297 #else /* HAVE_ANSISTDARG */
298 VOIDRET lreply
FUNCTION((n
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
), int n AND
char *fmt AND
int p0 AND
int p1 AND
int p2 AND
int p3 AND
int p4 AND
int p5
)
301 printf(fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
305 syslog(LOG_DEBUG
, "<--- %d- ", n
);
306 syslog(LOG_DEBUG
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
309 #endif /* HAVE_ANSISTDARG */
311 VOIDRET enable_signalling FUNCTION_NOARGS
313 signal(SIGPIPE
, lostconn
);
314 if ((int)signal(SIGURG
, myoob
) < 0)
315 syslog(LOG_ERR
, "signal: %m");
318 VOIDRET disable_signalling FUNCTION_NOARGS
320 signal(SIGPIPE
, SIG_IGN
);
321 if ((int)signal(SIGURG
, SIG_IGN
) < 0)
322 syslog(LOG_ERR
, "signal: %m");
325 static VOIDRET lostconn
FUNCTION((input
), int input
)
328 syslog(LOG_DEBUG
, "lost connection");
332 static char ttyline
[20];
335 * Helper function for sgetpwnam().
337 static char *sgetsave
FUNCTION((s
), char *s
)
339 char *new = malloc((unsigned) strlen(s
) + 1);
342 perror_reply(421, "Local resource failure: malloc");
351 * Save the result of a getpwnam. Used for USER command, since
352 * the data returned must not be clobbered by any other command
355 static struct passwd
*sgetpwnam
FUNCTION((name
), char *name
)
357 static struct passwd save
;
358 register struct passwd
*p
;
362 #endif /* HAVE_SHADOW */
364 if ((p
= getpwnam(name
)) == NULL
)
368 if ((spwd
= getspnam(name
)) == NULL
)
373 p
->pw_passwd
= spwd
->sp_pwdp
;
374 #endif /* HAVE_SHADOW */
380 free(save
.pw_passwd
);
386 save
.pw_name
= sgetsave(p
->pw_name
);
387 save
.pw_passwd
= sgetsave(p
->pw_passwd
);
388 save
.pw_gecos
= sgetsave(p
->pw_gecos
);
389 save
.pw_dir
= sgetsave(p
->pw_dir
);
390 save
.pw_shell
= sgetsave(p
->pw_shell
);
394 int login_attempts
; /* number of failed login attempts */
395 int askpasswd
; /* had user command, ask for passwd */
399 * Sets global passwd pointer pw if named account exists and is acceptable;
400 * sets askpasswd if a PASS command is expected. If logged in previously,
401 * need to reset state. If name is "ftp" or "anonymous", the name is not in
402 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
403 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
404 * requesting login privileges. Disallow anyone who does not have a standard
405 * shell as returned by getusershell(). Disallow anyone mentioned in the file
406 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
408 int user
FUNCTION((name
), char *name
)
416 reply(530, "Can't change user from guest login.");
419 #endif /* DOANONMOUS */
425 if (!strcmp(name
, "ftp") || !strcmp(name
, "anonymous"))
426 if (!checkuser("ftp") && !checkuser("anonymous"))
427 if ((pw
= sgetpwnam("ftp")) != NULL
) {
430 reply(331, "Guest login ok, send your e-mail address as your password.");
431 syslog(LOG_INFO
, "Anonymous FTP connection made from host %s.", remotehost
);
434 #endif /* DOANONYMOUS */
435 if (pw
= sgetpwnam(name
)) {
436 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
437 shell
= _PATH_BSHELL
;
438 while ((cp
= getusershell()) != NULL
)
439 if (!strcmp(cp
, shell
))
442 if (cp
== NULL
|| checkuser(name
) || ((pw
->pw_passwd
[0] == '*') || (pw
->pw_passwd
[0] == '#'))) {
445 syslog(LOG_DEBUG
, "Couldn't find %s in the list of valid shells.", pw
->pw_shell
);
447 syslog(LOG_DEBUG
, "checkuser failed - user in /etc/ftpusers?");
448 if (((pw
->pw_passwd
[0] == '*') || (pw
->pw_passwd
[0] == '#')))
449 syslog(LOG_DEBUG
, "Login disabled: pw_passwd == %s", pw
->pw_passwd
);
451 pw
= (struct passwd
*) NULL
;
456 char prompt
[OPIE_CHALLENGE_MAX
+ 1];
458 opiechallenge(&opiestate
, name
, prompt
);
460 if (askpasswd
== -1) {
461 syslog(LOG_WARNING
, "Invalid FTP user name %s attempted from %s.", name
, remotehost
);
464 pwok
= af_pwok
&& opiealways(pw
->pw_dir
);
467 reply(331, "Response to %s %s for %s.", prompt
,
468 #else /* NEW_PROMPTS */
469 reply(331, "OTP response %s %s for %s.", prompt
,
470 #endif /* NEW_PROMPTS */
471 pwok
? "requested" : "required", name
);
473 /* Delay before reading passwd after first failed attempt to slow down
474 passwd-guessing programs. */
476 sleep((unsigned) login_attempts
);
482 * Check if a user is in the file _PATH_FTPUSERS
484 static int checkuser
FUNCTION((name
), char *name
)
490 if ((fd
= fopen(_PATH_FTPUSERS
, "r")) != NULL
) {
491 while (fgets(line
, sizeof(line
), fd
) != NULL
)
492 if ((p
= strchr(line
, '\n')) != NULL
) {
496 if (!strcmp(line
, name
)) {
507 * Terminate login as previous user, if any, resetting state;
508 * used when USER command is given or login fails.
510 static VOIDRET end_login FUNCTION_NOARGS
512 disable_signalling();
513 if (seteuid((uid_t
) 0))
514 syslog(LOG_ERR
, "Can't set euid");
516 opielogwtmp(ttyline
, "", "", "ftp");
521 #endif /* DOANONYMOUS */
525 VOIDRET pass
FUNCTION((passwd
), char *passwd
)
527 int legit
= askpasswd
+ 1, i
;
529 if (logged_in
|| askpasswd
== 0) {
530 reply(503, "Login with USER first.");
536 if (!guest
) { /* "ftp" is only account allowed no password */
537 #endif /* DOANONYMOUS */
538 i
= opieverify(&opiestate
, passwd
);
539 if (legit
&& i
&& pwok
)
540 i
= strcmp(crypt(passwd
, pw
->pw_passwd
), pw
->pw_passwd
);
542 reply(530, "Login incorrect.");
544 if (login_attempts
++ >= 5) {
546 "Repeated login failures for user %s from %s",
547 pw
->pw_name
, remotehost
);
554 if ((passwd
[0] <= ' ') || checkuser(passwd
)) {
555 reply(530, "No identity, no service.");
556 syslog(LOG_DEBUG
, "Bogus address: %s", passwd
);
559 #endif /* DOANONYMOUS */
560 login_attempts
= 0; /* this time successful */
561 if (setegid((gid_t
) pw
->pw_gid
) < 0) {
562 reply(550, "Can't set gid.");
563 syslog(LOG_DEBUG
, "gid = %d, errno = %s(%d)", pw
->pw_gid
, strerror(errno
), errno
);
566 initgroups(pw
->pw_name
, pw
->pw_gid
);
568 /* open wtmp before chroot */
569 sprintf(ttyline
, "ftp%d", getpid());
570 opielogwtmp(ttyline
, pw
->pw_name
, remotehost
, "ftp");
575 /* We MUST do a chdir() after the chroot. Otherwise the old current
576 directory will be accessible as "." outside the new root! */
577 if (chroot(pw
->pw_dir
) < 0 || chdir("/") < 0) {
578 reply(550, "Can't set guest privileges.");
582 #endif /* DOANONYMOUS */
583 if (chdir(pw
->pw_dir
) < 0) {
584 if (chdir("/") < 0) {
585 reply(530, "User %s: can't change directory to %s.",
586 pw
->pw_name
, pw
->pw_dir
);
589 lreply(230, "No directory! Logging in with home=/");
591 /* This patch was contributed by an OPIE user. We don't know what it
592 does, exactly. It may or may not work. */
598 setgroups(NULL
, NULL
);
599 if (setpriv(PRIV_SET
|PRIV_INHERITED
|PRIV_EFFECTIVE
|PRIV_BEQUEATH
,
600 &priv
, sizeof(priv_t
)) < 0 ||
601 setgidx(ID_REAL
|ID_EFFECTIVE
, (gid_t
)pw
->pw_gid
) < 0 ||
602 setuidx(ID_REAL
|ID_EFFECTIVE
, (uid_t
)pw
->pw_uid
) < 0 ||
603 seteuid((uid_t
)pw
->pw_uid
) < 0) {
604 reply(550, "Can't set uid (_AIX3).");
609 if (seteuid((uid_t
) pw
->pw_uid
) < 0) {
610 reply(550, "Can't set uid.");
615 * Display a login message, if it exists.
616 * N.B. reply(230,) must follow the message.
621 if ((fd
= fopen(_PATH_FTPLOGINMESG
, "r")) != NULL
) {
624 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
625 if ((cp
= strchr(line
, '\n')) != NULL
)
627 lreply(230, "%s", line
);
629 (void) fflush(stdout
);
635 reply(230, "Guest login ok, access restrictions apply.");
637 setproctitle("%s: anonymous/%.*s", remotehost
,
638 sizeof(proctitle
) - sizeof(remotehost
) - sizeof(": anonymous/"),
641 syslog(LOG_NOTICE
, "ANONYMOUS FTP login from %s with ID %s",
644 #endif /* DOANONYMOUS */
646 reply(230, "User %s logged in.", pw
->pw_name
);
649 setproctitle("%s: %s", remotehost
, pw
->pw_name
);
651 syslog(LOG_INFO
, "FTP login from %s with user name %s", remotehost
, pw
->pw_name
);
653 home
= pw
->pw_dir
; /* home dir for globbing */
658 /* Forget all about it... */
662 VOIDRET retrieve
FUNCTION((cmd
, name
), char *cmd AND
char *name
)
669 fin
= fopen(name
, "r"), closefunc
= fclose
;
674 snprintf(line
, sizeof(line
), cmd
, name
);
676 fin
= ftpd_popen(line
, "r"), closefunc
= ftpd_pclose
;
679 st
.st_blksize
= BUFSIZ
;
680 #endif /* HAVE_ST_BLKSIZE */
684 perror_reply(550, name
);
688 (fstat(fileno(fin
), &st
) < 0 || (st
.st_mode
& S_IFMT
) != S_IFREG
)) {
689 reply(550, "%s: not a plain file.", name
);
693 if (type
== TYPE_A
) {
694 register int i
, n
, c
;
699 if ((c
= getc(fin
)) == EOF
) {
700 perror_reply(550, name
);
707 if (lseek(fileno(fin
), restart_point
, SEEK_SET
/* L_SET */ ) < 0) {
708 perror_reply(550, name
);
712 dout
= dataconn(name
, st
.st_size
, "w");
716 send_data(fin
, dout
, st
.st_blksize
);
717 #else /* HAVE_ST_BLKSIZE */
718 send_data(fin
, dout
, BUFSIZ
);
719 #endif /* HAVE_ST_BLKSIZE */
727 VOIDRET store
FUNCTION((name
, mode
, unique
), char *name AND
char *mode AND
int unique
)
733 if (unique
&& stat(name
, &st
) == 0 &&
734 (name
= gunique(name
)) == NULL
)
739 fout
= fopen(name
, mode
);
742 perror_reply(553, name
);
746 if (type
== TYPE_A
) {
747 register int i
, n
, c
;
752 if ((c
= getc(fout
)) == EOF
) {
753 perror_reply(550, name
);
759 /* We must do this seek to "current" position because we are changing
760 from reading to writing. */
761 if (fseek(fout
, 0L, SEEK_CUR
/* L_INCR */ ) < 0) {
762 perror_reply(550, name
);
766 if (lseek(fileno(fout
), restart_point
, SEEK_SET
/* L_SET */ ) < 0) {
767 perror_reply(550, name
);
771 din
= dataconn(name
, (off_t
) - 1, "r");
774 if (receive_data(din
, fout
) == 0) {
776 reply(226, "Transfer complete (unique file name:%s).",
779 reply(226, "Transfer complete.");
788 static FILE *getdatasock
FUNCTION((mode
), char *mode
)
790 int s
, on
= 1, tries
;
793 return (fdopen(data
, mode
));
794 disable_signalling();
795 if (seteuid((uid_t
) 0))
796 syslog(LOG_ERR
, "Can't set euid");
797 s
= socket(AF_INET
, SOCK_STREAM
, 0);
800 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
801 (char *) &on
, sizeof(on
)) < 0)
803 /* anchor socket to avoid multi-homing problems */
804 data_source
.sin_family
= AF_INET
;
805 data_source
.sin_addr
= ctrl_addr
.sin_addr
;
806 for (tries
= 1;; tries
++) {
807 if (bind(s
, (struct sockaddr
*) & data_source
,
808 sizeof(data_source
)) >= 0)
810 if (errno
!= EADDRINUSE
|| tries
> 10)
814 if (seteuid((uid_t
) pw
->pw_uid
))
815 syslog(LOG_ERR
, "Can't set euid");
818 on
= IPTOS_THROUGHPUT
;
819 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *) &on
, sizeof(int)) < 0)
820 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
822 return (fdopen(s
, mode
));
827 if (seteuid((uid_t
) pw
->pw_uid
))
828 syslog(LOG_ERR
, "Can't set euid");
837 static FILE *dataconn
FUNCTION((name
, size
, mode
), char *name AND off_t size AND
char *mode
)
848 if (size
!= (off_t
) - 1)
849 snprintf(sizebuf
, sizeof(sizebuf
), " (%ld bytes)", size
);
853 struct sockaddr_in from
;
854 int s
, fromlen
= sizeof(from
);
856 s
= accept(pdata
, (struct sockaddr
*) & from
, &fromlen
);
858 reply(425, "Can't open data connection.");
866 tos
= IPTOS_LOWDELAY
;
867 setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *) &tos
,
871 reply(150, "Opening %s mode data connection for %s%s.",
872 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
873 return (fdopen(pdata
, mode
));
876 reply(125, "Using existing data connection for %s%s.",
879 return (fdopen(data
, mode
));
882 data_dest
= his_addr
;
884 file
= getdatasock(mode
);
886 reply(425, "Can't create data socket (%s,%d): %s.",
887 inet_ntoa(data_source
.sin_addr
),
888 ntohs(data_source
.sin_port
), strerror(errno
));
892 while (connect(data
, (struct sockaddr
*) & data_dest
,
893 sizeof(data_dest
)) < 0) {
894 if (errno
== EADDRINUSE
&& retry
< swaitmax
) {
895 sleep((unsigned) swaitint
);
899 perror_reply(425, "Can't build data connection");
904 reply(150, "Opening %s mode data connection for %s%s.",
905 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
910 * Tranfer the contents of "instr" to
911 * "outstr" peer using the appropriate
912 * encapsulation of the data subject
913 * to Mode, Structure, and Type.
915 * NB: Form isn't handled.
917 static VOIDRET send_data
FUNCTION((instr
, outstr
, blksize
), FILE *instr AND
FILE *outstr AND off_t blksize
)
924 if (setjmp(urgcatch
)) {
931 while ((c
= getc(instr
)) != EOF
) {
946 reply(226, "Transfer complete.");
951 if ((buf
= malloc((u_int
) blksize
)) == NULL
) {
953 perror_reply(451, "Local resource failure: malloc");
956 netfd
= fileno(outstr
);
957 filefd
= fileno(instr
);
958 while ((cnt
= read(filefd
, buf
, (u_int
) blksize
)) > 0 &&
959 write(netfd
, buf
, cnt
) == cnt
)
968 reply(226, "Transfer complete.");
972 reply(550, "Unimplemented TYPE %d in send_data", type
);
978 perror_reply(426, "Data connection");
983 perror_reply(551, "Error on input file");
987 * Transfer data from peer to
988 * "outstr" using the appropriate
989 * encapulation of the data subject
990 * to Mode, Structure, and Type.
992 * N.B.: Form isn't handled.
994 static int receive_data
FUNCTION((instr
, outstr
), FILE *instr AND
FILE *outstr
)
997 int cnt
, bare_lfs
= 0;
1001 if (setjmp(urgcatch
)) {
1009 while ((cnt
= read(fileno(instr
), buf
, sizeof buf
)) > 0) {
1010 if (write(fileno(outstr
), buf
, cnt
) != cnt
)
1020 reply(553, "TYPE E not implemented.");
1025 while ((c
= getc(instr
)) != EOF
) {
1032 if ((c
= getc(instr
)) != '\n') {
1034 if (c
== '\0' || c
== EOF
)
1048 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs
);
1049 printf(" File may not have transferred correctly.\r\n");
1053 reply(550, "Unimplemented TYPE %d in receive_data", type
);
1060 perror_reply(426, "Data Connection");
1065 perror_reply(452, "Error writing file");
1069 VOIDRET statfilecmd
FUNCTION((filename
), char *filename
)
1076 snprintf(line
, sizeof(line
), "%s %s", "/bin/ls -lgA", filename
);
1077 #else /* HAVE_LS_G_FLAG */
1078 snprintf(line
, sizeof(line
), "%s %s", "/bin/ls -lA", filename
);
1079 #endif /* HAVE_LS_G_FLAG */
1080 fin
= ftpd_popen(line
, "r");
1081 lreply(211, "status of %s:", filename
);
1082 while ((c
= getc(fin
)) != EOF
) {
1084 if (ferror(stdout
)) {
1085 perror_reply(421, "control connection");
1091 perror_reply(551, filename
);
1100 reply(211, "End of Status");
1103 VOIDRET statcmd FUNCTION_NOARGS
1105 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1106 struct sockaddr_in
*sin
;
1109 lreply(211, "FTP server status:");
1111 printf(" Connected to %s", remotehost
);
1112 if (!isdigit(remotehost
[0]))
1113 printf(" (%s)", inet_ntoa(his_addr
.sin_addr
));
1118 printf(" Logged in anonymously\r\n");
1120 #endif /* DOANONYMOUS */
1121 printf(" Logged in as %s\r\n", pw
->pw_name
);
1124 printf(" Waiting for password\r\n");
1126 printf(" Waiting for user name\r\n");
1128 printf(" Data connection open\r\n");
1131 printf(" in Passive mode");
1135 if (usedefault
== 0) {
1139 a
= (u_char
*) & sin
->sin_addr
;
1140 p
= (u_char
*) & sin
->sin_port
;
1141 #define UC(b) (((int) b) & 0xff)
1142 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a
[0]),
1143 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1146 printf(" No data connection\r\n");
1147 reply(211, "End of status");
1150 VOIDRET opiefatal
FUNCTION((s
), char *s
)
1152 reply(451, "Error in server: %s\n", s
);
1153 reply(221, "Closing connection due to server error.");
1158 static VOIDRET ack
FUNCTION((s
), char *s
)
1160 reply(250, "%s command successful.", s
);
1163 VOIDRET nack
FUNCTION((s
), char *s
)
1165 reply(502, "%s command not implemented.", s
);
1168 VOIDRET yyerror
FUNCTION((s
), char *s
)
1172 if (cp
= strchr(cbuf
, '\n'))
1174 reply(500, "'%s': command not understood.", cbuf
);
1177 VOIDRET
delete FUNCTION((name
), char *name
)
1181 if (stat(name
, &st
) < 0) {
1182 perror_reply(550, name
);
1185 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
1186 if (rmdir(name
) < 0) {
1187 perror_reply(550, name
);
1192 if (unlink(name
) < 0) {
1193 perror_reply(550, name
);
1200 VOIDRET cwd
FUNCTION((path
), char *path
)
1202 if (chdir(path
) < 0)
1203 perror_reply(550, path
);
1208 VOIDRET makedir
FUNCTION((name
), char *name
)
1210 if (mkdir(name
, 0777) < 0)
1211 perror_reply(550, name
);
1213 reply(257, "MKD command successful.");
1216 VOIDRET removedir
FUNCTION((name
), char *name
)
1218 if (rmdir(name
) < 0)
1219 perror_reply(550, name
);
1224 VOIDRET pwd FUNCTION_NOARGS
1226 char path
[MAXPATHLEN
+ 1];
1228 if (getcwd(path
, MAXPATHLEN
) == (char *) NULL
)
1229 reply(550, "%s.", path
);
1231 reply(257, "\"%s\" is current directory.", path
);
1234 char *renamefrom
FUNCTION((name
), char *name
)
1238 if (stat(name
, &st
) < 0) {
1239 perror_reply(550, name
);
1240 return ((char *) 0);
1242 reply(350, "File exists, ready for destination name");
1246 VOIDRET renamecmd
FUNCTION((from
, to
), char *from AND
char *to
)
1248 if (rename(from
, to
) < 0)
1249 perror_reply(550, "rename");
1254 static VOIDRET dolog
FUNCTION((sin
), struct sockaddr_in
*sin
)
1256 struct hostent
*hp
= gethostbyaddr((char *) &sin
->sin_addr
,
1257 sizeof(struct in_addr
), AF_INET
);
1261 opiestrncpy(remotehost
, hp
->h_name
, sizeof(remotehost
));
1263 opiestrncpy(remotehost
, inet_ntoa(sin
->sin_addr
), sizeof(remotehost
));
1265 setproctitle("%s: connected", remotehost
);
1266 #endif /* DOTITLE */
1268 t
= time((time_t *) 0);
1269 syslog(LOG_INFO
, "connection from %s at %s",
1270 remotehost
, ctime(&t
));
1274 * Record logout in wtmp file
1275 * and exit with supplied status.
1277 VOIDRET dologout
FUNCTION((status
), int status
)
1279 disable_signalling();
1281 if (seteuid((uid_t
) 0))
1282 syslog(LOG_ERR
, "Can't set euid");
1283 opielogwtmp(ttyline
, "", "", "ftp");
1285 /* beware of flushing buffers after a SIGPIPE */
1289 static VOIDRET myoob
FUNCTION((input
), int input
)
1293 /* only process if transfer occurring */
1297 if (getline(cp
, 7, stdin
) == NULL
) {
1298 reply(221, "You could at least say goodbye.");
1302 if (strcmp(cp
, "ABOR\r\n") == 0) {
1304 reply(426, "Transfer aborted. Data connection closed.");
1305 reply(226, "Abort successful");
1306 longjmp(urgcatch
, 1);
1308 if (strcmp(cp
, "STAT\r\n") == 0) {
1309 if (file_size
!= (off_t
) - 1)
1310 reply(213, "Status: %lu of %lu bytes transferred",
1311 byte_count
, file_size
);
1313 reply(213, "Status: %lu bytes transferred", byte_count
);
1318 * Note: a response of 425 is not mentioned as a possible response to
1319 * the PASV command in RFC959. However, it has been blessed as
1320 * a legitimate response by Jon Postel in a telephone conversation
1321 * with Rick Adams on 25 Jan 89.
1323 VOIDRET passive FUNCTION_NOARGS
1326 register char *p
, *a
;
1328 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
1330 perror_reply(425, "Can't open passive connection");
1333 pasv_addr
= ctrl_addr
;
1334 pasv_addr
.sin_port
= 0;
1335 if (seteuid((uid_t
) 0))
1336 syslog(LOG_ERR
, "Can't set euid");
1337 if (bind(pdata
, (struct sockaddr
*) & pasv_addr
, sizeof(pasv_addr
)) < 0) {
1338 seteuid((uid_t
) pw
->pw_uid
);
1341 if (seteuid((uid_t
) pw
->pw_uid
))
1342 syslog(LOG_ERR
, "Can't set euid");
1343 len
= sizeof(pasv_addr
);
1344 if (getsockname(pdata
, (struct sockaddr
*) & pasv_addr
, &len
) < 0)
1346 if (listen(pdata
, 1) < 0)
1348 a
= (char *) &pasv_addr
.sin_addr
;
1349 p
= (char *) &pasv_addr
.sin_port
;
1351 #define UC(b) (((int) b) & 0xff)
1353 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
1354 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1360 perror_reply(425, "Can't open passive connection");
1365 * Generate unique name for file with basename "local".
1366 * The file named "local" is already known to exist.
1367 * Generates failure reply on error.
1369 static char *gunique
FUNCTION((local
), char *local
)
1371 static char new[MAXPATHLEN
+1];
1373 char *cp
= strrchr(local
, '/');
1378 if (stat(cp
? local
: ".", &st
) < 0) {
1379 perror_reply(553, cp
? local
: ".");
1380 return ((char *) 0);
1385 cp
= new + strlen(new);
1387 for (count
= 1; count
< 100; count
++) {
1388 snprintf(cp
, sizeof(new) - (cp
- new), "%d", count
);
1389 if (stat(new, &st
) < 0)
1392 reply(452, "Unique file name cannot be created.");
1393 return ((char *) 0);
1397 * Format and send reply containing system error number.
1399 VOIDRET perror_reply
FUNCTION((code
, string
), int code AND
char *string
)
1401 reply(code
, "%s: %s.", string
, strerror(errno
));
1404 static char *onefile
[] =
1410 VOIDRET send_file_list
FUNCTION((whichfiles
), char *whichfiles
)
1416 register char **dirlist
, *dirname
;
1419 if (strpbrk(whichfiles
, "~{[*?") != NULL
) {
1420 extern char **ftpglob(), *globerr
;
1423 dirlist
= ftpglob(whichfiles
);
1424 if (globerr
!= NULL
) {
1425 reply(550, globerr
);
1428 if (dirlist
== NULL
) {
1430 perror_reply(550, whichfiles
);
1434 onefile
[0] = whichfiles
;
1439 if (setjmp(urgcatch
)) {
1443 while (dirname
= *dirlist
++) {
1444 if (stat(dirname
, &st
) < 0) {
1445 /* If user typed "ls -l", etc, and the client used NLST, do what the
1447 if (dirname
[0] == '-' && *dirlist
== NULL
&&
1449 retrieve("/bin/ls %s", dirname
);
1452 perror_reply(550, whichfiles
);
1461 if ((st
.st_mode
& S_IFMT
) == S_IFREG
) {
1463 dout
= dataconn("file list", (off_t
) - 1, "w");
1468 fprintf(dout
, "%s%s\n", dirname
,
1469 type
== TYPE_A
? "\r" : "");
1470 byte_count
+= strlen(dirname
) + 1;
1473 if ((st
.st_mode
& S_IFMT
) != S_IFDIR
)
1476 if ((dirp
= opendir(dirname
)) == NULL
)
1479 while ((dir
= readdir(dirp
)) != NULL
) {
1480 char nbuf
[MAXPATHLEN
+1];
1482 if (dir
->d_name
[0] == '.' && (strlen(dir
->d_name
) == 1))
1484 if (dir
->d_name
[0] == '.' && dir
->d_name
[1] == '.' &&
1485 (strlen(dir
->d_name
) == 2))
1488 snprintf(nbuf
, sizeof(nbuf
), "%s/%s", dirname
, dir
->d_name
);
1490 /* We have to do a stat to insure it's not a directory or special file. */
1491 if (simple
|| (stat(nbuf
, &st
) == 0 &&
1492 (st
.st_mode
& S_IFMT
) == S_IFREG
)) {
1494 dout
= dataconn("file list", (off_t
) - 1, "w");
1499 if (nbuf
[0] == '.' && nbuf
[1] == '/')
1500 fprintf(dout
, "%s%s\n", &nbuf
[2],
1501 type
== TYPE_A
? "\r" : "");
1503 fprintf(dout
, "%s%s\n", nbuf
,
1504 type
== TYPE_A
? "\r" : "");
1505 byte_count
+= strlen(nbuf
) + 1;
1512 reply(550, "No files found.");
1514 if (ferror(dout
) != 0)
1515 perror_reply(550, "Data connection");
1517 reply(226, "Transfer complete.");
1528 * clobber argv so ps will show what we're doing.
1529 * (stolen from sendmail)
1530 * warning, since this is usually started from inetd.conf, it
1531 * often doesn't have much of an environment or arglist to overwrite.
1533 VOIDRET setproctitle
FUNCTION((fmt
, a
, b
, c
), char *fmt AND
int a AND
int b AND
int c
)
1535 register char *p
, *bp
, ch
;
1539 snprintf(buf
, sizeof(buf
), fmt
, a
, b
, c
);
1541 /* make ps print our process name */
1546 if (i
> LastArgv
- p
- 2) {
1547 i
= LastArgv
- p
- 2;
1552 if (ch
!= '\n' && ch
!= '\r')
1554 while (p
< LastArgv
)
1557 #endif /* DOTITLE */
1559 VOIDRET catchexit FUNCTION_NOARGS
1564 int main
FUNCTION((argc
, argv
, envp
), int argc AND
char *argv
[] AND
char *envp
[])
1566 int addrlen
, on
= 1;
1575 for (i
= sysconf(_SC_OPEN_MAX
); i
> 2; i
--)
1579 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1580 anonymous ftp's that chroot and can't do it later. */
1581 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
1583 addrlen
= sizeof(his_addr
);
1584 if (getpeername(0, (struct sockaddr
*) & his_addr
, &addrlen
) < 0) {
1585 syslog(LOG_ERR
, "getpeername (%s): %m", argv
[0]);
1588 addrlen
= sizeof(ctrl_addr
);
1589 if (getsockname(0, (struct sockaddr
*) & ctrl_addr
, &addrlen
) < 0) {
1590 syslog(LOG_ERR
, "getsockname (%s): %m", argv
[0]);
1594 tos
= IPTOS_LOWDELAY
;
1595 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *) &tos
, sizeof(int)) < 0)
1596 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
1598 data_source
.sin_port
= htons(ntohs(ctrl_addr
.sin_port
) - 1);
1601 /* Save start and extent of argv for setproctitle. */
1605 LastArgv
= envp
[-1] + strlen(envp
[-1]);
1606 #endif /* DOTITLE */
1609 while (argc
> 0 && *argv
[0] == '-') {
1610 for (cp
= &argv
[0][1]; *cp
; cp
++)
1625 timeout
= atoi(++cp
);
1626 if (maxtimeout
< timeout
)
1627 maxtimeout
= timeout
;
1631 maxtimeout
= atoi(++cp
);
1632 if (timeout
> maxtimeout
)
1633 timeout
= maxtimeout
;
1640 while (*++cp
&& *cp
>= '0' && *cp
<= '9')
1641 val
= val
* 8 + *cp
- '0';
1643 fprintf(stderr
, "ftpd: Bad value for -u\n");
1650 fprintf(stderr
, "ftpd: Unknown flag -%c ignored.\n",
1657 freopen(_PATH_DEVNULL
, "w", stderr
);
1658 signal(SIGCHLD
, SIG_IGN
);
1659 enable_signalling();
1661 /* Try to handle urgent data inline */
1663 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *) &on
, sizeof(on
)) < 0)
1664 syslog(LOG_ERR
, "setsockopt: %m");
1668 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
1669 syslog(LOG_ERR
, "fcntl F_SETOWN: %m");
1672 /* Set up default state */
1679 af_pwok
= opieaccessfile(remotehost
);
1685 /* If logins are disabled, print out the message. */
1686 if ((fd
= fopen(_PATH_NOLOGIN
,"r")) != NULL
) {
1687 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
1688 if ((cp
= strchr(line
, '\n')) != NULL
)
1690 lreply(530, "%s", line
);
1692 (void) fflush(stdout
);
1694 reply(530, "System not available.");
1697 if ((fd
= fopen(_PATH_FTPWELCOME
, "r")) != NULL
) {
1698 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
1699 if ((cp
= strchr(line
, '\n')) != NULL
)
1701 lreply(220, "%s", line
);
1703 (void) fflush(stdout
);
1705 /* reply(220,) must follow */
1709 reply(220, "FTP server ready.");