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: head/contrib/opie/opieftpd.c 92914 2002-03-21 23:42:52Z markm $
54 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55 * All rights reserved.
57 * Redistribution and use in source and binary forms, with or without
58 * modification, are permitted provided that the following conditions
60 * 1. Redistributions of source code must retain the above copyright
61 * notice, this list of conditions and the following disclaimer.
62 * 2. Redistributions in binary form must reproduce the above copyright
63 * notice, this list of conditions and the following disclaimer in the
64 * documentation and/or other materials provided with the distribution.
65 * 3. All advertising materials mentioning features or use of this software
66 * must display the following acknowledgement:
67 * This product includes software developed by the University of
68 * California, Berkeley and its contributors.
69 * 4. Neither the name of the University nor the names of its contributors
70 * may be used to endorse or promote products derived from this software
71 * without specific prior written permission.
73 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
90 #endif /* HAVE_ANSISTDARG */
97 #include <sys/param.h>
98 #endif /* HAVE_SYS_PARAM_H */
100 /* #include <sys/ioctl.h> */
101 #include <sys/socket.h>
102 #include <sys/wait.h>
104 #include <sys/fcntl.h>
107 #endif /* SYS_FCNTL_H */
108 #include <sys/types.h>
110 #include <netinet/in.h>
111 #include <netinet/in_systm.h>
112 #include <netinet/ip.h>
115 #include <arpa/ftp.h>
116 #include <arpa/inet.h>
117 #include <arpa/telnet.h>
123 #endif /* HAVE_TIME_H */
126 #endif /* HAVE_PWD_H */
133 #endif /* HAVE_UNISTD_H */
144 #endif /* HAVE_SHADOW_H */
148 #endif /* HAVE_CRYPT_H */
150 #if HAVE_SYS_UTSNAME_H
151 #include <sys/utsname.h>
152 #endif /* HAVE_SYS_UTSNAME_H */
156 #include <sys/priv.h>
160 #ifndef IPTOS_THROUGHPUT
162 #endif /* !IPTOS_THROUGHPUT */
163 #ifndef IPTOS_LOWDELAY
165 #endif /* !IPTOS_LOWDELAY */
169 extern char *home
; /* pointer to home directory for glob */
170 extern FILE *ftpd_popen
__P((char *, char *));
171 extern int ftpd_pclose
__P((FILE *));
173 extern off_t restart_point
;
175 static struct sockaddr_in ctrl_addr
;
176 static struct sockaddr_in data_source
;
177 struct sockaddr_in data_dest
;
178 struct sockaddr_in his_addr
;
179 static struct sockaddr_in pasv_addr
;
183 static jmp_buf urgcatch
;
187 int timeout
= 900; /* timeout after 15 minutes of inactivity */
188 int maxtimeout
= 7200; /* don't allow idle time to be set beyond 2 hours */
192 #endif /* DOANONYMOUS */
195 static int stru
; /* avoid C keyword */
197 int usedefault
= 1; /* for data transfers */
198 int pdata
= -1; /* for passive mode */
199 static int transflag
;
200 static off_t file_size
;
201 static off_t byte_count
;
203 #if (!defined(CMASK) || CMASK == 0)
208 static int defumask
= CMASK
; /* default umask value */
210 char remotehost
[MAXHOSTNAMELEN
];
213 * Timeout intervals for retrying connections
214 * to hosts that don't accept PORT cmds. This
215 * is a kludge, but given the problems with TCP...
217 #define SWAITMAX 90 /* wait at most 90 seconds */
218 #define SWAITINT 5 /* interval between retries */
220 static int swaitmax
= SWAITMAX
;
221 static int swaitint
= SWAITINT
;
224 static char **Argv
= NULL
; /* pointer to argument vector */
225 static char *LastArgv
= NULL
; /* end of argv */
226 static char proctitle
[BUFSIZ
]; /* initial part of title */
229 static int af_pwok
= 0, pwok
= 0;
230 static struct opie opiestate
;
232 VOIDRET perror_reply
__P((int, char *));
233 VOIDRET dologout
__P((int));
234 char *getline
__P((char *, int, FILE *));
235 VOIDRET upper
__P((char *));
237 static VOIDRET lostconn
__P((int));
238 static VOIDRET myoob
__P((int));
239 static FILE *getdatasock
__P((char *));
240 static FILE *dataconn
__P((char *, off_t
, char *));
241 static int checkuser
__P((char *));
242 static VOIDRET end_login
__P((void));
243 static VOIDRET send_data
__P((FILE *, FILE *, off_t
));
244 static int receive_data
__P((FILE *, FILE *));
245 static char *gunique
__P((char *));
246 static char *sgetsave
__P((char *));
248 int opielogwtmp
__P((char *, char *, char *, char *));
250 int fclose
__P((FILE *));
252 #ifdef HAVE_ANSISTDARG
253 VOIDRET reply
FUNCTION((stdarg is ANSI only
), int n AND
char *fmt AND
...)
259 vsprintf(buffer
, fmt
, ap
);
262 printf("%d %s\r\n", n
, buffer
);
265 syslog(LOG_DEBUG
, "<--- %d %s", n
, buffer
);
267 #else /* HAVE_ANSISTDARG */
268 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
)
271 printf(fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
275 syslog(LOG_DEBUG
, "<--- %d ", n
);
276 syslog(LOG_DEBUG
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
279 #endif /* HAVE_ANSISTDARG */
281 #ifdef HAVE_ANSISTDARG
282 VOIDRET lreply
FUNCTION((stdarg is ANSI only
), int n AND
char *fmt AND
...)
288 vsprintf(buffer
, fmt
, ap
);
291 printf("%d- %s\r\n", n
, buffer
);
294 syslog(LOG_DEBUG
, "<--- %d- %s", n
, buffer
);
296 #else /* HAVE_ANSISTDARG */
297 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
)
300 printf(fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
304 syslog(LOG_DEBUG
, "<--- %d- ", n
);
305 syslog(LOG_DEBUG
, fmt
, p0
, p1
, p2
, p3
, p4
, p5
);
308 #endif /* HAVE_ANSISTDARG */
310 VOIDRET enable_signalling FUNCTION_NOARGS
312 signal(SIGPIPE
, lostconn
);
313 if ((int)signal(SIGURG
, myoob
) < 0)
314 syslog(LOG_ERR
, "signal: %m");
317 VOIDRET disable_signalling FUNCTION_NOARGS
319 signal(SIGPIPE
, SIG_IGN
);
320 if ((int)signal(SIGURG
, SIG_IGN
) < 0)
321 syslog(LOG_ERR
, "signal: %m");
324 static VOIDRET lostconn
FUNCTION((input
), int input
)
327 syslog(LOG_DEBUG
, "lost connection");
331 static char ttyline
[20];
334 * Helper function for sgetpwnam().
336 static char *sgetsave
FUNCTION((s
), char *s
)
338 char *new = malloc((unsigned) strlen(s
) + 1);
341 perror_reply(421, "Local resource failure: malloc");
350 * Save the result of a getpwnam. Used for USER command, since
351 * the data returned must not be clobbered by any other command
354 static struct passwd
*sgetpwnam
FUNCTION((name
), char *name
)
356 static struct passwd save
;
357 register struct passwd
*p
;
361 #endif /* HAVE_SHADOW */
363 if ((p
= getpwnam(name
)) == NULL
)
367 if ((spwd
= getspnam(name
)) == NULL
)
372 p
->pw_passwd
= spwd
->sp_pwdp
;
373 #endif /* HAVE_SHADOW */
379 free(save
.pw_passwd
);
385 save
.pw_name
= sgetsave(p
->pw_name
);
386 save
.pw_passwd
= sgetsave(p
->pw_passwd
);
387 save
.pw_gecos
= sgetsave(p
->pw_gecos
);
388 save
.pw_dir
= sgetsave(p
->pw_dir
);
389 save
.pw_shell
= sgetsave(p
->pw_shell
);
393 int login_attempts
; /* number of failed login attempts */
394 int askpasswd
; /* had user command, ask for passwd */
398 * Sets global passwd pointer pw if named account exists and is acceptable;
399 * sets askpasswd if a PASS command is expected. If logged in previously,
400 * need to reset state. If name is "ftp" or "anonymous", the name is not in
401 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
403 * requesting login privileges. Disallow anyone who does not have a standard
404 * shell as returned by getusershell(). Disallow anyone mentioned in the file
405 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
407 int user
FUNCTION((name
), char *name
)
415 reply(530, "Can't change user from guest login.");
418 #endif /* DOANONMOUS */
424 if (!strcmp(name
, "ftp") || !strcmp(name
, "anonymous"))
425 if (!checkuser("ftp") && !checkuser("anonymous"))
426 if ((pw
= sgetpwnam("ftp")) != NULL
) {
429 reply(331, "Guest login ok, send your e-mail address as your password.");
430 syslog(LOG_INFO
, "Anonymous FTP connection made from host %s.", remotehost
);
433 #endif /* DOANONYMOUS */
434 if (pw
= sgetpwnam(name
)) {
435 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
436 shell
= _PATH_BSHELL
;
437 while ((cp
= getusershell()) != NULL
)
438 if (!strcmp(cp
, shell
))
441 if (cp
== NULL
|| checkuser(name
) || ((pw
->pw_passwd
[0] == '*') || (pw
->pw_passwd
[0] == '#'))) {
444 syslog(LOG_DEBUG
, "Couldn't find %s in the list of valid shells.", pw
->pw_shell
);
446 syslog(LOG_DEBUG
, "checkuser failed - user in /etc/ftpusers?");
447 if (((pw
->pw_passwd
[0] == '*') || (pw
->pw_passwd
[0] == '#')))
448 syslog(LOG_DEBUG
, "Login disabled: pw_passwd == %s", pw
->pw_passwd
);
450 pw
= (struct passwd
*) NULL
;
455 char prompt
[OPIE_CHALLENGE_MAX
+ 1];
457 opiechallenge(&opiestate
, name
, prompt
);
459 if (askpasswd
== -1) {
460 syslog(LOG_WARNING
, "Invalid FTP user name %s attempted from %s.", name
, remotehost
);
463 pwok
= af_pwok
&& opiealways(pw
->pw_dir
);
466 reply(331, "Response to %s %s for %s.", prompt
,
467 #else /* NEW_PROMPTS */
468 reply(331, "OTP response %s %s for %s.", prompt
,
469 #endif /* NEW_PROMPTS */
470 pwok
? "requested" : "required", name
);
472 /* Delay before reading passwd after first failed attempt to slow down
473 passwd-guessing programs. */
475 sleep((unsigned) login_attempts
);
481 * Check if a user is in the file _PATH_FTPUSERS
483 static int checkuser
FUNCTION((name
), char *name
)
489 if ((fd
= fopen(_PATH_FTPUSERS
, "r")) != NULL
) {
490 while (fgets(line
, sizeof(line
), fd
) != NULL
)
491 if ((p
= strchr(line
, '\n')) != NULL
) {
495 if (!strcmp(line
, name
)) {
506 * Terminate login as previous user, if any, resetting state;
507 * used when USER command is given or login fails.
509 static VOIDRET end_login FUNCTION_NOARGS
511 disable_signalling();
512 if (seteuid((uid_t
) 0))
513 syslog(LOG_ERR
, "Can't set euid");
515 opielogwtmp(ttyline
, "", "", "ftp");
520 #endif /* DOANONYMOUS */
524 VOIDRET pass
FUNCTION((passwd
), char *passwd
)
526 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 cryptpw
= crypt(passwd
, pw
->pw_passwd
);
541 i
= (cryptpw
== NULL
|| strcmp(cryptpw
, pw
->pw_passwd
) != 0);
544 reply(530, "Login incorrect.");
546 if (login_attempts
++ >= 5) {
548 "Repeated login failures for user %s from %s",
549 pw
->pw_name
, remotehost
);
556 if ((passwd
[0] <= ' ') || checkuser(passwd
)) {
557 reply(530, "No identity, no service.");
558 syslog(LOG_DEBUG
, "Bogus address: %s", passwd
);
561 #endif /* DOANONYMOUS */
562 login_attempts
= 0; /* this time successful */
563 if (setegid((gid_t
) pw
->pw_gid
) < 0) {
564 reply(550, "Can't set gid.");
565 syslog(LOG_DEBUG
, "gid = %d, errno = %s(%d)", pw
->pw_gid
, strerror(errno
), errno
);
568 initgroups(pw
->pw_name
, pw
->pw_gid
);
570 /* open wtmp before chroot */
571 sprintf(ttyline
, "ftp%d", getpid());
572 opielogwtmp(ttyline
, pw
->pw_name
, remotehost
, "ftp");
577 /* We MUST do a chdir() after the chroot. Otherwise the old current
578 directory will be accessible as "." outside the new root! */
579 if (chroot(pw
->pw_dir
) < 0 || chdir("/") < 0) {
580 reply(550, "Can't set guest privileges.");
584 #endif /* DOANONYMOUS */
585 if (chdir(pw
->pw_dir
) < 0) {
586 if (chdir("/") < 0) {
587 reply(530, "User %s: can't change directory to %s.",
588 pw
->pw_name
, pw
->pw_dir
);
591 lreply(230, "No directory! Logging in with home=/");
593 /* This patch was contributed by an OPIE user. We don't know what it
594 does, exactly. It may or may not work. */
600 setgroups(NULL
, NULL
);
601 if (setpriv(PRIV_SET
|PRIV_INHERITED
|PRIV_EFFECTIVE
|PRIV_BEQUEATH
,
602 &priv
, sizeof(priv_t
)) < 0 ||
603 setgidx(ID_REAL
|ID_EFFECTIVE
, (gid_t
)pw
->pw_gid
) < 0 ||
604 setuidx(ID_REAL
|ID_EFFECTIVE
, (uid_t
)pw
->pw_uid
) < 0 ||
605 seteuid((uid_t
)pw
->pw_uid
) < 0) {
606 reply(550, "Can't set uid (_AIX3).");
611 if (seteuid((uid_t
) pw
->pw_uid
) < 0) {
612 reply(550, "Can't set uid.");
617 * Display a login message, if it exists.
618 * N.B. reply(230,) must follow the message.
623 if ((fd
= fopen(_PATH_FTPLOGINMESG
, "r")) != NULL
) {
626 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
627 if ((cp
= strchr(line
, '\n')) != NULL
)
629 lreply(230, "%s", line
);
631 (void) fflush(stdout
);
637 reply(230, "Guest login ok, access restrictions apply.");
639 setproctitle("%s: anonymous/%.*s", remotehost
,
640 sizeof(proctitle
) - sizeof(remotehost
) - sizeof(": anonymous/"),
643 syslog(LOG_NOTICE
, "ANONYMOUS FTP login from %s with ID %s",
646 #endif /* DOANONYMOUS */
648 reply(230, "User %s logged in.", pw
->pw_name
);
651 setproctitle("%s: %s", remotehost
, pw
->pw_name
);
653 syslog(LOG_INFO
, "FTP login from %s with user name %s", remotehost
, pw
->pw_name
);
655 home
= pw
->pw_dir
; /* home dir for globbing */
660 /* Forget all about it... */
664 VOIDRET retrieve
FUNCTION((cmd
, name
), char *cmd AND
char *name
)
671 fin
= fopen(name
, "r"), closefunc
= fclose
;
676 snprintf(line
, sizeof(line
), cmd
, name
);
678 fin
= ftpd_popen(line
, "r"), closefunc
= ftpd_pclose
;
681 st
.st_blksize
= BUFSIZ
;
682 #endif /* HAVE_ST_BLKSIZE */
686 perror_reply(550, name
);
690 (fstat(fileno(fin
), &st
) < 0 || (st
.st_mode
& S_IFMT
) != S_IFREG
)) {
691 reply(550, "%s: not a plain file.", name
);
695 if (type
== TYPE_A
) {
696 register int i
, n
, c
;
701 if ((c
= getc(fin
)) == EOF
) {
702 perror_reply(550, name
);
709 if (lseek(fileno(fin
), restart_point
, SEEK_SET
/* L_SET */ ) < 0) {
710 perror_reply(550, name
);
714 dout
= dataconn(name
, st
.st_size
, "w");
718 send_data(fin
, dout
, st
.st_blksize
);
719 #else /* HAVE_ST_BLKSIZE */
720 send_data(fin
, dout
, BUFSIZ
);
721 #endif /* HAVE_ST_BLKSIZE */
729 VOIDRET store
FUNCTION((name
, mode
, unique
), char *name AND
char *mode AND
int unique
)
735 if (unique
&& stat(name
, &st
) == 0 &&
736 (name
= gunique(name
)) == NULL
)
741 fout
= fopen(name
, mode
);
744 perror_reply(553, name
);
748 if (type
== TYPE_A
) {
749 register int i
, n
, c
;
754 if ((c
= getc(fout
)) == EOF
) {
755 perror_reply(550, name
);
761 /* We must do this seek to "current" position because we are changing
762 from reading to writing. */
763 if (fseek(fout
, 0L, SEEK_CUR
/* L_INCR */ ) < 0) {
764 perror_reply(550, name
);
768 if (lseek(fileno(fout
), restart_point
, SEEK_SET
/* L_SET */ ) < 0) {
769 perror_reply(550, name
);
773 din
= dataconn(name
, (off_t
) - 1, "r");
776 if (receive_data(din
, fout
) == 0) {
778 reply(226, "Transfer complete (unique file name:%s).",
781 reply(226, "Transfer complete.");
790 static FILE *getdatasock
FUNCTION((mode
), char *mode
)
792 int s
, on
= 1, tries
;
795 return (fdopen(data
, mode
));
796 disable_signalling();
797 if (seteuid((uid_t
) 0))
798 syslog(LOG_ERR
, "Can't set euid");
799 s
= socket(AF_INET
, SOCK_STREAM
, 0);
802 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
803 (char *) &on
, sizeof(on
)) < 0)
805 /* anchor socket to avoid multi-homing problems */
806 data_source
.sin_family
= AF_INET
;
807 data_source
.sin_addr
= ctrl_addr
.sin_addr
;
808 for (tries
= 1;; tries
++) {
809 if (bind(s
, (struct sockaddr
*) & data_source
,
810 sizeof(data_source
)) >= 0)
812 if (errno
!= EADDRINUSE
|| tries
> 10)
816 if (seteuid((uid_t
) pw
->pw_uid
))
817 syslog(LOG_ERR
, "Can't set euid");
820 on
= IPTOS_THROUGHPUT
;
821 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *) &on
, sizeof(int)) < 0)
822 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
824 return (fdopen(s
, mode
));
829 if (seteuid((uid_t
) pw
->pw_uid
))
830 syslog(LOG_ERR
, "Can't set euid");
839 static FILE *dataconn
FUNCTION((name
, size
, mode
), char *name AND off_t size AND
char *mode
)
850 if (size
!= (off_t
) - 1)
851 snprintf(sizebuf
, sizeof(sizebuf
), " (%ld bytes)", size
);
855 struct sockaddr_in from
;
856 int s
, fromlen
= sizeof(from
);
858 s
= accept(pdata
, (struct sockaddr
*) & from
, &fromlen
);
860 reply(425, "Can't open data connection.");
868 tos
= IPTOS_LOWDELAY
;
869 setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *) &tos
,
873 reply(150, "Opening %s mode data connection for %s%s.",
874 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
875 return (fdopen(pdata
, mode
));
878 reply(125, "Using existing data connection for %s%s.",
881 return (fdopen(data
, mode
));
884 data_dest
= his_addr
;
886 file
= getdatasock(mode
);
888 reply(425, "Can't create data socket (%s,%d): %s.",
889 inet_ntoa(data_source
.sin_addr
),
890 ntohs(data_source
.sin_port
), strerror(errno
));
894 while (connect(data
, (struct sockaddr
*) & data_dest
,
895 sizeof(data_dest
)) < 0) {
896 if (errno
== EADDRINUSE
&& retry
< swaitmax
) {
897 sleep((unsigned) swaitint
);
901 perror_reply(425, "Can't build data connection");
906 reply(150, "Opening %s mode data connection for %s%s.",
907 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
912 * Tranfer the contents of "instr" to
913 * "outstr" peer using the appropriate
914 * encapsulation of the data subject
915 * to Mode, Structure, and Type.
917 * NB: Form isn't handled.
919 static VOIDRET send_data
FUNCTION((instr
, outstr
, blksize
), FILE *instr AND
FILE *outstr AND off_t blksize
)
926 if (setjmp(urgcatch
)) {
933 while ((c
= getc(instr
)) != EOF
) {
948 reply(226, "Transfer complete.");
953 if ((buf
= malloc((u_int
) blksize
)) == NULL
) {
955 perror_reply(451, "Local resource failure: malloc");
958 netfd
= fileno(outstr
);
959 filefd
= fileno(instr
);
960 while ((cnt
= read(filefd
, buf
, (u_int
) blksize
)) > 0 &&
961 write(netfd
, buf
, cnt
) == cnt
)
970 reply(226, "Transfer complete.");
974 reply(550, "Unimplemented TYPE %d in send_data", type
);
980 perror_reply(426, "Data connection");
985 perror_reply(551, "Error on input file");
989 * Transfer data from peer to
990 * "outstr" using the appropriate
991 * encapulation of the data subject
992 * to Mode, Structure, and Type.
994 * N.B.: Form isn't handled.
996 static int receive_data
FUNCTION((instr
, outstr
), FILE *instr AND
FILE *outstr
)
999 int cnt
, bare_lfs
= 0;
1003 if (setjmp(urgcatch
)) {
1011 while ((cnt
= read(fileno(instr
), buf
, sizeof buf
)) > 0) {
1012 if (write(fileno(outstr
), buf
, cnt
) != cnt
)
1022 reply(553, "TYPE E not implemented.");
1027 while ((c
= getc(instr
)) != EOF
) {
1034 if ((c
= getc(instr
)) != '\n') {
1036 if (c
== '\0' || c
== EOF
)
1050 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs
);
1051 printf(" File may not have transferred correctly.\r\n");
1055 reply(550, "Unimplemented TYPE %d in receive_data", type
);
1062 perror_reply(426, "Data Connection");
1067 perror_reply(452, "Error writing file");
1071 VOIDRET statfilecmd
FUNCTION((filename
), char *filename
)
1078 snprintf(line
, sizeof(line
), "%s %s", "/bin/ls -lgA", filename
);
1079 #else /* HAVE_LS_G_FLAG */
1080 snprintf(line
, sizeof(line
), "%s %s", "/bin/ls -lA", filename
);
1081 #endif /* HAVE_LS_G_FLAG */
1082 fin
= ftpd_popen(line
, "r");
1083 lreply(211, "status of %s:", filename
);
1084 while ((c
= getc(fin
)) != EOF
) {
1086 if (ferror(stdout
)) {
1087 perror_reply(421, "control connection");
1093 perror_reply(551, filename
);
1102 reply(211, "End of Status");
1105 VOIDRET statcmd FUNCTION_NOARGS
1107 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1108 struct sockaddr_in
*sin
;
1111 lreply(211, "FTP server status:");
1113 printf(" Connected to %s", remotehost
);
1114 if (!isdigit(remotehost
[0]))
1115 printf(" (%s)", inet_ntoa(his_addr
.sin_addr
));
1120 printf(" Logged in anonymously\r\n");
1122 #endif /* DOANONYMOUS */
1123 printf(" Logged in as %s\r\n", pw
->pw_name
);
1126 printf(" Waiting for password\r\n");
1128 printf(" Waiting for user name\r\n");
1130 printf(" Data connection open\r\n");
1133 printf(" in Passive mode");
1137 if (usedefault
== 0) {
1141 a
= (u_char
*) & sin
->sin_addr
;
1142 p
= (u_char
*) & sin
->sin_port
;
1143 #define UC(b) (((int) b) & 0xff)
1144 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a
[0]),
1145 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1148 printf(" No data connection\r\n");
1149 reply(211, "End of status");
1152 VOIDRET opiefatal
FUNCTION((s
), char *s
)
1154 reply(451, "Error in server: %s\n", s
);
1155 reply(221, "Closing connection due to server error.");
1160 static VOIDRET ack
FUNCTION((s
), char *s
)
1162 reply(250, "%s command successful.", s
);
1165 VOIDRET nack
FUNCTION((s
), char *s
)
1167 reply(502, "%s command not implemented.", s
);
1170 VOIDRET yyerror
FUNCTION((s
), char *s
)
1174 if (cp
= strchr(cbuf
, '\n'))
1176 reply(500, "'%s': command not understood.", cbuf
);
1179 VOIDRET
delete FUNCTION((name
), char *name
)
1183 if (stat(name
, &st
) < 0) {
1184 perror_reply(550, name
);
1187 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
1188 if (rmdir(name
) < 0) {
1189 perror_reply(550, name
);
1194 if (unlink(name
) < 0) {
1195 perror_reply(550, name
);
1202 VOIDRET cwd
FUNCTION((path
), char *path
)
1204 if (chdir(path
) < 0)
1205 perror_reply(550, path
);
1210 VOIDRET makedir
FUNCTION((name
), char *name
)
1212 if (mkdir(name
, 0777) < 0)
1213 perror_reply(550, name
);
1215 reply(257, "MKD command successful.");
1218 VOIDRET removedir
FUNCTION((name
), char *name
)
1220 if (rmdir(name
) < 0)
1221 perror_reply(550, name
);
1226 VOIDRET pwd FUNCTION_NOARGS
1228 char path
[MAXPATHLEN
+ 1];
1230 if (getcwd(path
, MAXPATHLEN
) == (char *) NULL
)
1231 reply(550, "%s.", path
);
1233 reply(257, "\"%s\" is current directory.", path
);
1236 char *renamefrom
FUNCTION((name
), char *name
)
1240 if (stat(name
, &st
) < 0) {
1241 perror_reply(550, name
);
1242 return ((char *) 0);
1244 reply(350, "File exists, ready for destination name");
1248 VOIDRET renamecmd
FUNCTION((from
, to
), char *from AND
char *to
)
1250 if (rename(from
, to
) < 0)
1251 perror_reply(550, "rename");
1256 static VOIDRET dolog
FUNCTION((sin
), struct sockaddr_in
*sin
)
1258 struct hostent
*hp
= gethostbyaddr((char *) &sin
->sin_addr
,
1259 sizeof(struct in_addr
), AF_INET
);
1263 opiestrncpy(remotehost
, hp
->h_name
, sizeof(remotehost
));
1265 opiestrncpy(remotehost
, inet_ntoa(sin
->sin_addr
), sizeof(remotehost
));
1267 setproctitle("%s: connected", remotehost
);
1268 #endif /* DOTITLE */
1270 t
= time((time_t *) 0);
1271 syslog(LOG_INFO
, "connection from %s at %s",
1272 remotehost
, ctime(&t
));
1276 * Record logout in wtmp file
1277 * and exit with supplied status.
1279 VOIDRET dologout
FUNCTION((status
), int status
)
1281 disable_signalling();
1283 if (seteuid((uid_t
) 0))
1284 syslog(LOG_ERR
, "Can't set euid");
1285 opielogwtmp(ttyline
, "", "", "ftp");
1287 /* beware of flushing buffers after a SIGPIPE */
1291 static VOIDRET myoob
FUNCTION((input
), int input
)
1295 /* only process if transfer occurring */
1299 if (getline(cp
, 7, stdin
) == NULL
) {
1300 reply(221, "You could at least say goodbye.");
1304 if (strcmp(cp
, "ABOR\r\n") == 0) {
1306 reply(426, "Transfer aborted. Data connection closed.");
1307 reply(226, "Abort successful");
1308 longjmp(urgcatch
, 1);
1310 if (strcmp(cp
, "STAT\r\n") == 0) {
1311 if (file_size
!= (off_t
) - 1)
1312 reply(213, "Status: %lu of %lu bytes transferred",
1313 byte_count
, file_size
);
1315 reply(213, "Status: %lu bytes transferred", byte_count
);
1320 * Note: a response of 425 is not mentioned as a possible response to
1321 * the PASV command in RFC959. However, it has been blessed as
1322 * a legitimate response by Jon Postel in a telephone conversation
1323 * with Rick Adams on 25 Jan 89.
1325 VOIDRET passive FUNCTION_NOARGS
1328 register char *p
, *a
;
1330 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
1332 perror_reply(425, "Can't open passive connection");
1335 pasv_addr
= ctrl_addr
;
1336 pasv_addr
.sin_port
= 0;
1337 if (seteuid((uid_t
) 0))
1338 syslog(LOG_ERR
, "Can't set euid");
1339 if (bind(pdata
, (struct sockaddr
*) & pasv_addr
, sizeof(pasv_addr
)) < 0) {
1340 seteuid((uid_t
) pw
->pw_uid
);
1343 if (seteuid((uid_t
) pw
->pw_uid
))
1344 syslog(LOG_ERR
, "Can't set euid");
1345 len
= sizeof(pasv_addr
);
1346 if (getsockname(pdata
, (struct sockaddr
*) & pasv_addr
, &len
) < 0)
1348 if (listen(pdata
, 1) < 0)
1350 a
= (char *) &pasv_addr
.sin_addr
;
1351 p
= (char *) &pasv_addr
.sin_port
;
1353 #define UC(b) (((int) b) & 0xff)
1355 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
1356 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
1362 perror_reply(425, "Can't open passive connection");
1367 * Generate unique name for file with basename "local".
1368 * The file named "local" is already known to exist.
1369 * Generates failure reply on error.
1371 static char *gunique
FUNCTION((local
), char *local
)
1373 static char new[MAXPATHLEN
+1];
1375 char *cp
= strrchr(local
, '/');
1380 if (stat(cp
? local
: ".", &st
) < 0) {
1381 perror_reply(553, cp
? local
: ".");
1382 return ((char *) 0);
1387 cp
= new + strlen(new);
1389 for (count
= 1; count
< 100; count
++) {
1390 snprintf(cp
, sizeof(new) - (cp
- new), "%d", count
);
1391 if (stat(new, &st
) < 0)
1394 reply(452, "Unique file name cannot be created.");
1395 return ((char *) 0);
1399 * Format and send reply containing system error number.
1401 VOIDRET perror_reply
FUNCTION((code
, string
), int code AND
char *string
)
1403 reply(code
, "%s: %s.", string
, strerror(errno
));
1406 static char *onefile
[] =
1412 VOIDRET send_file_list
FUNCTION((whichfiles
), char *whichfiles
)
1418 register char **dirlist
, *dirname
;
1421 if (strpbrk(whichfiles
, "~{[*?") != NULL
) {
1422 extern char **ftpglob(), *globerr
;
1425 dirlist
= ftpglob(whichfiles
);
1426 if (globerr
!= NULL
) {
1427 reply(550, globerr
);
1430 if (dirlist
== NULL
) {
1432 perror_reply(550, whichfiles
);
1436 onefile
[0] = whichfiles
;
1441 if (setjmp(urgcatch
)) {
1445 while (dirname
= *dirlist
++) {
1446 if (stat(dirname
, &st
) < 0) {
1447 /* If user typed "ls -l", etc, and the client used NLST, do what the
1449 if (dirname
[0] == '-' && *dirlist
== NULL
&&
1451 retrieve("/bin/ls %s", dirname
);
1454 perror_reply(550, whichfiles
);
1463 if ((st
.st_mode
& S_IFMT
) == S_IFREG
) {
1465 dout
= dataconn("file list", (off_t
) - 1, "w");
1470 fprintf(dout
, "%s%s\n", dirname
,
1471 type
== TYPE_A
? "\r" : "");
1472 byte_count
+= strlen(dirname
) + 1;
1475 if ((st
.st_mode
& S_IFMT
) != S_IFDIR
)
1478 if ((dirp
= opendir(dirname
)) == NULL
)
1481 while ((dir
= readdir(dirp
)) != NULL
) {
1482 char nbuf
[MAXPATHLEN
+1];
1484 if (dir
->d_name
[0] == '.' && (strlen(dir
->d_name
) == 1))
1486 if (dir
->d_name
[0] == '.' && dir
->d_name
[1] == '.' &&
1487 (strlen(dir
->d_name
) == 2))
1490 snprintf(nbuf
, sizeof(nbuf
), "%s/%s", dirname
, dir
->d_name
);
1492 /* We have to do a stat to insure it's not a directory or special file. */
1493 if (simple
|| (stat(nbuf
, &st
) == 0 &&
1494 (st
.st_mode
& S_IFMT
) == S_IFREG
)) {
1496 dout
= dataconn("file list", (off_t
) - 1, "w");
1501 if (nbuf
[0] == '.' && nbuf
[1] == '/')
1502 fprintf(dout
, "%s%s\n", &nbuf
[2],
1503 type
== TYPE_A
? "\r" : "");
1505 fprintf(dout
, "%s%s\n", nbuf
,
1506 type
== TYPE_A
? "\r" : "");
1507 byte_count
+= strlen(nbuf
) + 1;
1514 reply(550, "No files found.");
1516 if (ferror(dout
) != 0)
1517 perror_reply(550, "Data connection");
1519 reply(226, "Transfer complete.");
1530 * clobber argv so ps will show what we're doing.
1531 * (stolen from sendmail)
1532 * warning, since this is usually started from inetd.conf, it
1533 * often doesn't have much of an environment or arglist to overwrite.
1535 VOIDRET setproctitle
FUNCTION((fmt
, a
, b
, c
), char *fmt AND
int a AND
int b AND
int c
)
1537 register char *p
, *bp
, ch
;
1541 snprintf(buf
, sizeof(buf
), fmt
, a
, b
, c
);
1543 /* make ps print our process name */
1548 if (i
> LastArgv
- p
- 2) {
1549 i
= LastArgv
- p
- 2;
1554 if (ch
!= '\n' && ch
!= '\r')
1556 while (p
< LastArgv
)
1559 #endif /* DOTITLE */
1561 VOIDRET catchexit FUNCTION_NOARGS
1566 int main
FUNCTION((argc
, argv
, envp
), int argc AND
char *argv
[] AND
char *envp
[])
1568 int addrlen
, on
= 1;
1577 for (i
= sysconf(_SC_OPEN_MAX
); i
> 2; i
--)
1581 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1582 anonymous ftp's that chroot and can't do it later. */
1583 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
1585 addrlen
= sizeof(his_addr
);
1586 if (getpeername(0, (struct sockaddr
*) & his_addr
, &addrlen
) < 0) {
1587 syslog(LOG_ERR
, "getpeername (%s): %m", argv
[0]);
1590 addrlen
= sizeof(ctrl_addr
);
1591 if (getsockname(0, (struct sockaddr
*) & ctrl_addr
, &addrlen
) < 0) {
1592 syslog(LOG_ERR
, "getsockname (%s): %m", argv
[0]);
1596 tos
= IPTOS_LOWDELAY
;
1597 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *) &tos
, sizeof(int)) < 0)
1598 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
1600 data_source
.sin_port
= htons(ntohs(ctrl_addr
.sin_port
) - 1);
1603 /* Save start and extent of argv for setproctitle. */
1607 LastArgv
= envp
[-1] + strlen(envp
[-1]);
1608 #endif /* DOTITLE */
1611 while (argc
> 0 && *argv
[0] == '-') {
1612 for (cp
= &argv
[0][1]; *cp
; cp
++)
1627 timeout
= atoi(++cp
);
1628 if (maxtimeout
< timeout
)
1629 maxtimeout
= timeout
;
1633 maxtimeout
= atoi(++cp
);
1634 if (timeout
> maxtimeout
)
1635 timeout
= maxtimeout
;
1642 while (*++cp
&& *cp
>= '0' && *cp
<= '9')
1643 val
= val
* 8 + *cp
- '0';
1645 fprintf(stderr
, "ftpd: Bad value for -u\n");
1652 fprintf(stderr
, "ftpd: Unknown flag -%c ignored.\n",
1659 freopen(_PATH_DEVNULL
, "w", stderr
);
1660 signal(SIGCHLD
, SIG_IGN
);
1661 enable_signalling();
1663 /* Try to handle urgent data inline */
1665 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *) &on
, sizeof(on
)) < 0)
1666 syslog(LOG_ERR
, "setsockopt: %m");
1670 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
1671 syslog(LOG_ERR
, "fcntl F_SETOWN: %m");
1674 /* Set up default state */
1681 af_pwok
= opieaccessfile(remotehost
);
1687 /* If logins are disabled, print out the message. */
1688 if ((fd
= fopen(_PATH_NOLOGIN
,"r")) != NULL
) {
1689 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
1690 if ((cp
= strchr(line
, '\n')) != NULL
)
1692 lreply(530, "%s", line
);
1694 (void) fflush(stdout
);
1696 reply(530, "System not available.");
1699 if ((fd
= fopen(_PATH_FTPWELCOME
, "r")) != NULL
) {
1700 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
1701 if ((cp
= strchr(line
, '\n')) != NULL
)
1703 lreply(220, "%s", line
);
1705 (void) fflush(stdout
);
1707 /* reply(220,) must follow */
1711 reply(220, "FTP server ready.");