1 /* $NetBSD: ftpd.c,v 1.200 2013/07/31 19:50:47 christos Exp $ */
4 * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
34 * The Regents of the University of California. All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * Copyright (C) 1997 and 1998 WIDE Project.
63 * All rights reserved.
65 * Redistribution and use in source and binary forms, with or without
66 * modification, are permitted provided that the following conditions
68 * 1. Redistributions of source code must retain the above copyright
69 * notice, this list of conditions and the following disclaimer.
70 * 2. Redistributions in binary form must reproduce the above copyright
71 * notice, this list of conditions and the following disclaimer in the
72 * documentation and/or other materials provided with the distribution.
73 * 3. Neither the name of the project nor the names of its contributors
74 * may be used to endorse or promote products derived from this software
75 * without specific prior written permission.
77 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
90 #include <sys/cdefs.h>
92 __COPYRIGHT("@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\
93 The Regents of the University of California. All rights reserved.");
98 static char sccsid
[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
100 __RCSID("$NetBSD: ftpd.c,v 1.200 2013/07/31 19:50:47 christos Exp $");
102 #endif /* not lint */
107 #include <sys/param.h>
108 #include <sys/stat.h>
109 #include <sys/ioctl.h>
110 #include <sys/socket.h>
111 #include <sys/wait.h>
112 #include <sys/mman.h>
113 #include <sys/resource.h>
115 #include <netinet/in.h>
116 #include <netinet/in_systm.h>
117 #include <netinet/ip.h>
120 #include <arpa/ftp.h>
121 #include <arpa/inet.h>
122 #include <arpa/telnet.h>
157 #include <krb5/krb5.h>
161 #include <login_cap.h>
165 #include <security/pam_appl.h>
170 #include "pathnames.h"
173 static sig_atomic_t transflag
;
174 static sig_atomic_t urgflag
;
179 int stru
; /* avoid C keyword */
181 int dataport
; /* use specific data port */
182 int dopidfile
; /* maintain pid file */
183 int doutmp
; /* update utmp file */
184 int dowtmp
; /* update wtmp file */
185 int doxferlog
; /* syslog/write wu-ftpd style xferlog entries */
186 int xferlogfd
; /* fd to write wu-ftpd xferlog entries to */
187 int getnameopts
; /* flags for use with getname() */
188 int dropprivs
; /* if privileges should or have been dropped */
189 int mapped
; /* IPv4 connection on AF_INET6 socket */
192 static char ttyline
[20];
195 static int auth_pam(void);
196 pam_handle_t
*pamh
= NULL
;
200 static struct utmp utmp
; /* for utmp */
203 static struct utmpx utmpx
; /* for utmpx */
206 static const char *anondir
= NULL
;
207 static const char *confdir
= _DEFAULT_CONFDIR
;
209 static char *curname
; /* current USER name */
210 static size_t curname_len
; /* length of curname (include NUL) */
212 #if defined(KERBEROS) || defined(KERBEROS5)
215 char *krbtkfile_env
= NULL
;
217 int login_krb5_forwardable_tgt
= 0;
223 * Timeout intervals for retrying connections
224 * to hosts that don't accept PORT cmds. This
225 * is a kludge, but given the problems with TCP...
227 #define SWAITMAX 90 /* wait at most 90 seconds */
228 #define SWAITINT 5 /* interval between retries */
230 int swaitmax
= SWAITMAX
;
231 int swaitint
= SWAITINT
;
235 SS_ABORTED
, /* transfer aborted */
236 SS_NO_TRANSFER
, /* no transfer made yet */
237 SS_FILE_ERROR
, /* file read error */
238 SS_DATA_ERROR
/* data send error */
241 static int bind_pasv_addr(void);
242 static int checkuser(const char *, const char *, int, int, char **);
243 static int checkaccess(const char *);
244 static int checkpassword(const struct passwd
*, const char *);
245 static void do_pass(int, int, const char *);
246 static void end_login(void);
247 static FILE *getdatasock(const char *);
248 static char *gunique(const char *);
249 static void login_utmp(const char *, const char *, const char *,
251 static void logremotehost(struct sockinet
*);
252 __dead
static void lostconn(int);
253 __dead
static void toolong(int);
254 __dead
static void sigquit(int);
255 static void sigurg(int);
256 static int handleoobcmd(void);
257 static int receive_data(FILE *, FILE *);
258 static int send_data(FILE *, FILE *, const struct stat
*, int);
259 static struct passwd
*sgetpwnam(const char *);
260 static int write_data(int, char *, size_t, off_t
*, struct timeval
*,
262 static enum send_status
263 send_data_with_read(int, int, const struct stat
*, int);
264 #if !defined(__minix)
265 static enum send_status
266 send_data_with_mmap(int, int, const struct stat
*, int);
267 static void logrusage(const struct rusage
*, const struct rusage
*);
268 #endif /* !defined(__minix) */
269 static void logout_utmp(void);
271 int main(int, char *[]);
273 #if defined(KERBEROS)
274 int klogin(struct passwd
*, char *, char *, char *);
277 #if defined(KERBEROS5)
278 int k5login(struct passwd
*, char *, char *, char *);
279 void k5destroy(void);
283 main(int argc
, char *argv
[])
285 int ch
, on
= 1, tos
, keepalive
;
288 krb5_error_code kerror
;
291 const char *xferlogname
= NULL
;
294 sa_family_t af
= AF_UNSPEC
;
303 dopidfile
= 1; /* default: DO use a pid file to count users */
304 doutmp
= 0; /* default: Do NOT log to utmp */
305 dowtmp
= 1; /* default: DO log to wtmp */
306 doxferlog
= 0; /* default: Do NOT syslog xferlog */
307 xferlogfd
= -1; /* default: Do NOT write xferlog file */
308 getnameopts
= 0; /* default: xlate addrs to name */
317 version
= FTPD_VERSION
;
320 * LOG_NDELAY sets up the logging connection immediately,
321 * necessary for anonymous ftp's that chroot and can't do it later.
323 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
325 while ((ch
= getopt(argc
, argv
,
326 "46a:c:C:Dde:h:HlL:nP:qQrst:T:uUvV:wWX")) != -1) {
345 if ((p
= strchr(optarg
, '@')) != NULL
) {
347 strlcpy(remotehost
, p
, MAXHOSTNAMELEN
+ 1);
348 if (inet_pton(AF_INET
, p
,
349 &his_addr
.su_addr
) == 1) {
350 his_addr
.su_family
= AF_INET
;
352 sizeof(his_addr
.si_su
.su_sin
);
354 } else if (inet_pton(AF_INET6
, p
,
355 &his_addr
.su_6addr
) == 1) {
356 his_addr
.su_family
= AF_INET6
;
358 sizeof(his_addr
.si_su
.su_sin6
);
361 his_addr
.su_family
= AF_UNSPEC
;
363 pw
= sgetpwnam(optarg
);
364 exit(checkaccess(optarg
) ? 0 : 1);
372 case 'v': /* deprecated */
381 strlcpy(hostname
, optarg
, sizeof(hostname
));
385 if (gethostname(hostname
, sizeof(hostname
)) == -1)
387 hostname
[sizeof(hostname
) - 1] = '\0';
391 logging
++; /* > 1 == extra logging */
395 xferlogname
= optarg
;
399 getnameopts
= NI_NUMERICHOST
;
405 l
= strtol(optarg
, &p
, 10);
406 if (errno
|| *optarg
== '\0' || *p
!= '\0' ||
407 l
< IPPORT_RESERVED
||
408 l
> IPPORT_ANONMAX
) {
409 syslog(LOG_WARNING
, "Invalid dataport %s",
435 "-%c has been deprecated in favour of ftpd.conf",
448 if (EMPTYSTR(optarg
) || strcmp(optarg
, "-") == 0)
451 version
= ftpd_strdup(optarg
);
467 if (optopt
== 'a' || optopt
== 'C')
469 syslog(LOG_WARNING
, "unknown flag -%c ignored", optopt
);
473 if (EMPTYSTR(confdir
))
474 confdir
= _DEFAULT_CONFDIR
;
485 #if !defined(__minix)
486 l
= sysconf(_SC_LOGIN_NAME_MAX
);
487 if (l
== -1 && errno
!= 0) {
488 syslog(LOG_ERR
, "sysconf _SC_LOGIN_NAME_MAX: %m");
491 syslog(LOG_WARNING
, "using conservative LOGIN_NAME_MAX value");
492 curname_len
= _POSIX_LOGIN_NAME_MAX
;
494 curname_len
= (size_t)l
;
496 curname_len
= _POSIX_LOGIN_NAME_MAX
;
497 #endif /* !defined(__minix) */
498 curname
= malloc(curname_len
);
499 if (curname
== NULL
) {
500 syslog(LOG_ERR
, "malloc: %m");
506 int error
, fd
, i
, n
, *socks
;
508 struct addrinfo hints
, *res
, *res0
;
510 if (daemon(1, 0) == -1) {
511 syslog(LOG_ERR
, "failed to daemonize: %m");
514 (void)memset(&sa
, 0, sizeof(sa
));
515 sa
.sa_handler
= SIG_IGN
;
516 sa
.sa_flags
= SA_NOCLDWAIT
;
517 sigemptyset(&sa
.sa_mask
);
518 (void)sigaction(SIGCHLD
, &sa
, NULL
);
520 (void)memset(&hints
, 0, sizeof(hints
));
521 hints
.ai_flags
= AI_PASSIVE
;
522 hints
.ai_family
= af
;
523 hints
.ai_socktype
= SOCK_STREAM
;
524 error
= getaddrinfo(NULL
, "ftp", &hints
, &res0
);
526 syslog(LOG_ERR
, "getaddrinfo: %s", gai_strerror(error
));
530 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
)
533 syslog(LOG_ERR
, "no addresses available");
536 socks
= malloc(n
* sizeof(int));
537 fds
= malloc(n
* sizeof(struct pollfd
));
538 if (socks
== NULL
|| fds
== NULL
) {
539 syslog(LOG_ERR
, "malloc: %m");
543 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
) {
544 socks
[n
] = socket(res
->ai_family
, res
->ai_socktype
,
548 (void)setsockopt(socks
[n
], SOL_SOCKET
, SO_REUSEADDR
,
550 if (bind(socks
[n
], res
->ai_addr
, res
->ai_addrlen
)
552 (void)close(socks
[n
]);
555 if (listen(socks
[n
], 12) == -1) {
556 (void)close(socks
[n
]);
560 fds
[n
].fd
= socks
[n
];
561 fds
[n
].events
= POLLIN
;
565 syslog(LOG_ERR
, "%m");
570 if (pidfile(NULL
) == -1)
571 syslog(LOG_ERR
, "failed to write a pid file: %m");
574 if (poll(fds
, n
, INFTIM
) == -1) {
577 syslog(LOG_ERR
, "poll: %m");
580 for (i
= 0; i
< n
; i
++) {
581 if (fds
[i
].revents
& POLLIN
) {
582 fd
= accept(fds
[i
].fd
, NULL
, NULL
);
584 syslog(LOG_ERR
, "accept: %m");
589 syslog(LOG_ERR
, "fork: %m");
600 (void)dup2(fd
, STDIN_FILENO
);
601 (void)dup2(fd
, STDOUT_FILENO
);
602 (void)dup2(fd
, STDERR_FILENO
);
603 for (i
= 0; i
< n
; i
++)
604 (void)close(socks
[i
]);
607 memset((char *)&his_addr
, 0, sizeof(his_addr
));
608 addrlen
= sizeof(his_addr
.si_su
);
609 if (getpeername(0, (struct sockaddr
*)&his_addr
.si_su
, &addrlen
) < 0) {
610 syslog((errno
== ENOTCONN
) ? LOG_NOTICE
: LOG_ERR
,
611 "getpeername (%s): %m",argv
[0]);
614 his_addr
.su_len
= addrlen
;
615 memset((char *)&ctrl_addr
, 0, sizeof(ctrl_addr
));
616 addrlen
= sizeof(ctrl_addr
.si_su
);
617 if (getsockname(0, (struct sockaddr
*)&ctrl_addr
, &addrlen
) < 0) {
618 syslog(LOG_ERR
, "getsockname (%s): %m",argv
[0]);
621 ctrl_addr
.su_len
= addrlen
;
623 if (his_addr
.su_family
== AF_INET6
624 && IN6_IS_ADDR_V4MAPPED(&his_addr
.su_6addr
)) {
627 * IPv4 control connection arrived to AF_INET6 socket.
628 * I hate to do this, but this is the easiest solution.
630 * The assumption is untrue on SIIT environment.
632 struct sockinet tmp_addr
;
633 const int off
= sizeof(struct in6_addr
) - sizeof(struct in_addr
);
636 memset(&his_addr
, 0, sizeof(his_addr
));
637 his_addr
.su_family
= AF_INET
;
638 his_addr
.su_len
= sizeof(his_addr
.si_su
.su_sin
);
639 memcpy(&his_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
640 sizeof(his_addr
.su_addr
));
641 his_addr
.su_port
= tmp_addr
.su_port
;
643 tmp_addr
= ctrl_addr
;
644 memset(&ctrl_addr
, 0, sizeof(ctrl_addr
));
645 ctrl_addr
.su_family
= AF_INET
;
646 ctrl_addr
.su_len
= sizeof(ctrl_addr
.si_su
.su_sin
);
647 memcpy(&ctrl_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
648 sizeof(ctrl_addr
.su_addr
));
649 ctrl_addr
.su_port
= tmp_addr
.su_port
;
651 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
652 if ((cp
= strchr(line
, '\n')) != NULL
)
654 reply(-530, "%s", line
);
656 (void) fflush(stdout
);
659 "Connection from IPv4 mapped address is not supported.");
668 if (!mapped
&& his_addr
.su_family
== AF_INET
) {
669 tos
= IPTOS_LOWDELAY
;
670 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
672 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
675 /* if the hostname hasn't been given, attempt to determine it */
676 if (hostname
[0] == '\0') {
677 if (getnameinfo((struct sockaddr
*)&ctrl_addr
.si_su
,
678 ctrl_addr
.su_len
, hostname
, sizeof(hostname
), NULL
, 0,
680 (void)gethostname(hostname
, sizeof(hostname
));
681 hostname
[sizeof(hostname
) - 1] = '\0';
684 /* set this here so klogin can use it... */
685 (void)snprintf(ttyline
, sizeof(ttyline
), "ftp%d", getpid());
687 (void) freopen(_PATH_DEVNULL
, "w", stderr
);
689 memset(&sa
, 0, sizeof(sa
));
690 sa
.sa_handler
= SIG_DFL
;
691 sa
.sa_flags
= SA_RESTART
;
692 sigemptyset(&sa
.sa_mask
);
693 (void) sigaction(SIGCHLD
, &sa
, NULL
);
695 sa
.sa_handler
= sigquit
;
696 sa
.sa_flags
= SA_RESTART
;
697 sigfillset(&sa
.sa_mask
); /* block all sigs in these handlers */
698 (void) sigaction(SIGHUP
, &sa
, NULL
);
699 (void) sigaction(SIGINT
, &sa
, NULL
);
700 (void) sigaction(SIGQUIT
, &sa
, NULL
);
701 (void) sigaction(SIGTERM
, &sa
, NULL
);
702 sa
.sa_handler
= lostconn
;
703 (void) sigaction(SIGPIPE
, &sa
, NULL
);
704 sa
.sa_handler
= toolong
;
705 (void) sigaction(SIGALRM
, &sa
, NULL
);
706 sa
.sa_handler
= sigurg
;
707 #if !defined(__minix)
708 (void) sigaction(SIGURG
, &sa
, NULL
);
710 /* Try to handle urgent data inline */
712 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *)&on
, sizeof(on
)) < 0)
713 syslog(LOG_WARNING
, "setsockopt: %m");
715 /* Set keepalives on the socket to detect dropped connections. */
718 if (setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&keepalive
,
720 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
722 #endif /* !defined(__minix) */
725 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
726 syslog(LOG_WARNING
, "fcntl F_SETOWN: %m");
728 logremotehost(&his_addr
);
730 * Set up default state
741 kerror
= krb5_init_context(&kcontext
);
743 syslog(LOG_ERR
, "%s when initializing Kerberos context",
744 error_message(kerror
));
747 #endif /* KERBEROS5 */
750 curclass
.timeout
= 300; /* 5 minutes, as per login(1) */
751 curclass
.type
= CLASS_REAL
;
753 /* If logins are disabled, print out the message. */
754 if (display_file(_PATH_NOLOGIN
, 530)) {
755 reply(530, "System not available.");
758 (void)display_file(conffilename(_NAME_FTPWELCOME
), 220);
759 /* reply(220,) must follow */
760 if (EMPTYSTR(version
))
761 reply(220, "%s FTP server ready.", hostname
);
763 reply(220, "%s FTP server (%s) ready.", hostname
, version
);
765 if (xferlogname
!= NULL
) {
766 xferlogfd
= open(xferlogname
, O_WRONLY
| O_APPEND
| O_CREAT
,
769 syslog(LOG_WARNING
, "open xferlog `%s': %m",
780 lostconn(int signo __unused
)
784 syslog(LOG_DEBUG
, "lost connection");
789 toolong(int signo __unused
)
794 "Timeout (" LLF
" seconds): closing control connection.",
795 (LLT
)curclass
.timeout
);
797 syslog(LOG_INFO
, "User %s timed out after " LLF
" seconds",
798 (pw
? pw
->pw_name
: "unknown"), (LLT
)curclass
.timeout
);
807 syslog(LOG_DEBUG
, "got signal %d", signo
);
812 sigurg(int signo __unused
)
820 * Save the result of a getpwnam. Used for USER command, since
821 * the data returned must not be clobbered by any other command
824 static struct passwd
*
825 sgetpwnam(const char *name
)
827 static struct passwd save
;
830 if ((p
= getpwnam(name
)) == NULL
)
833 free((char *)save
.pw_name
);
834 memset(save
.pw_passwd
, 0, strlen(save
.pw_passwd
));
835 free((char *)save
.pw_passwd
);
836 free((char *)save
.pw_gecos
);
837 free((char *)save
.pw_dir
);
838 free((char *)save
.pw_shell
);
841 save
.pw_name
= ftpd_strdup(p
->pw_name
);
842 save
.pw_passwd
= ftpd_strdup(p
->pw_passwd
);
843 save
.pw_gecos
= ftpd_strdup(p
->pw_gecos
);
844 save
.pw_dir
= ftpd_strdup(p
->pw_dir
);
845 save
.pw_shell
= ftpd_strdup(p
->pw_shell
);
849 static int login_attempts
; /* number of failed login attempts */
850 static int askpasswd
; /* had USER command, ask for PASSwd */
851 static int permitted
; /* USER permitted */
855 * Sets global passwd pointer pw if named account exists and is acceptable;
856 * sets askpasswd if a PASS command is expected. If logged in previously,
857 * need to reset state. If name is "ftp" or "anonymous", the name is not in
858 * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
859 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
860 * requesting login privileges. Disallow anyone who does not have a standard
861 * shell as returned by getusershell(). Disallow anyone mentioned in the file
862 * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
865 user(const char *name
)
869 login_cap_t
*lc
= NULL
;
877 switch (curclass
.type
) {
879 reply(530, "Can't change user from guest login.");
882 reply(530, "Can't change user from chroot user.");
886 reply(530, "Can't change user.");
896 #if defined(KERBEROS)
899 #if defined(KERBEROS5)
903 curclass
.type
= CLASS_REAL
;
907 if (strcmp(name
, "ftp") == 0 || strcmp(name
, "anonymous") == 0) {
908 /* need `pw' setup for checkaccess() and checkuser () */
909 if ((pw
= sgetpwnam("ftp")) == NULL
)
910 reply(530, "User %s unknown.", name
);
911 else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
912 reply(530, "User %s access denied.", name
);
914 curclass
.type
= CLASS_GUEST
;
917 "Guest login ok, type your name as password.");
922 "ANONYMOUS FTP LOGIN REFUSED FROM %s",
929 pw
= sgetpwnam(name
);
931 strlcpy(curname
, name
, curname_len
);
933 /* check user in /etc/ftpusers, and setup class */
934 permitted
= checkuser(_NAME_FTPUSERS
, curname
, 1, 0, &class);
936 /* check user in /etc/ftpchroot */
938 lc
= login_getpwclass(pw
);
940 if (checkuser(_NAME_FTPCHROOT
, curname
, 0, 0, NULL
)
941 #ifdef LOGIN_CAP /* Allow login.conf configuration as well */
942 || login_getcapbool(lc
, "ftp-chroot", 0)
945 if (curclass
.type
== CLASS_GUEST
) {
947 "Can't change guest user to chroot class; remove entry in %s",
951 curclass
.type
= CLASS_CHROOT
;
954 /* determine default class */
956 switch (curclass
.type
) {
958 class = ftpd_strdup("guest");
961 class = ftpd_strdup("chroot");
964 class = ftpd_strdup("real");
967 syslog(LOG_ERR
, "unknown curclass.type %d; aborting",
972 /* parse ftpd.conf, setting up various parameters */
974 /* if not guest user, check for valid shell */
978 const char *cp
, *shell
;
980 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
981 shell
= _PATH_BSHELL
;
982 while ((cp
= getusershell()) != NULL
)
983 if (strcmp(cp
, shell
) == 0)
986 if (cp
== NULL
&& curclass
.type
!= CLASS_GUEST
)
990 /* deny quickly (after USER not PASS) if requested */
991 if (CURCLASS_FLAGS_ISSET(denyquick
) && !permitted
) {
992 reply(530, "User %s may not use FTP.", curname
);
994 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
995 remoteloghost
, curname
);
1000 /* if haven't asked yet (i.e, not anon), ask now */
1004 e
= auth_pam(); /* this does reply(331, ...) */
1007 #else /* !USE_PAM */
1009 if (skey_haskey(curname
) == 0) {
1012 myskey
= skey_keyinfo(curname
);
1013 reply(331, "Password [ %s ] required for %s.",
1014 myskey
? myskey
: "error getting challenge",
1018 reply(331, "Password required for %s.", curname
);
1019 #endif /* !USE_PAM */
1027 * Delay before reading passwd after first failed
1028 * attempt to slow down passwd-guessing programs.
1031 sleep((unsigned) login_attempts
);
1038 * Determine whether something is to happen (allow access, chroot)
1039 * for a user. Each line is a shell-style glob followed by
1042 * For backward compatibility, `allow' and `deny' are synonymns
1043 * for `yes' and `no', respectively.
1045 * Each glob is matched against the username in turn, and the first
1046 * match found is used. If no match is found, the result is the
1047 * argument `def'. If a match is found but without and explicit
1048 * `yes'/`no', the result is the opposite of def.
1050 * If the file doesn't exist at all, the result is the argument
1053 * Any line starting with `#' is considered a comment and ignored.
1055 * Returns 0 if the user is denied, or 1 if they are allowed.
1057 * NOTE: needs struct passwd *pw setup before use.
1060 checkuser(const char *fname
, const char *name
, int def
, int nofile
,
1065 char *word
, *perm
, *class, *buf
, *p
;
1069 if (retclass
!= NULL
)
1071 if ((fd
= fopen(conffilename(fname
), "r")) == NULL
)
1076 (buf
= fparseln(fd
, &len
, &line
, NULL
, FPARSELN_UNESCCOMM
|
1077 FPARSELN_UNESCCONT
| FPARSELN_UNESCESC
)) != NULL
;
1078 free(buf
), buf
= NULL
) {
1079 word
= perm
= class = NULL
;
1083 if (p
[len
- 1] == '\n')
1093 if (!EMPTYSTR(class)) {
1094 if (strcasecmp(class, "all") == 0 ||
1095 strcasecmp(class, "none") == 0) {
1097 "%s line %d: illegal user-defined class `%s' - skipping entry",
1098 fname
, (int)line
, class);
1103 /* have a host specifier */
1104 if ((p
= strchr(word
, '@')) != NULL
) {
1105 unsigned char net
[16], mask
[16], *addr
;
1106 int addrlen
, bits
, bytes
, a
;
1109 /* check against network or CIDR */
1110 memset(net
, 0x00, sizeof(net
));
1111 if ((bits
= inet_net_pton(his_addr
.su_family
, p
, net
,
1112 sizeof(net
))) != -1) {
1114 if (his_addr
.su_family
== AF_INET
) {
1117 addr
= (unsigned char *)&his_addr
.su_addr
;
1121 addr
= (unsigned char *)&his_addr
.su_6addr
;
1127 memset(mask
, 0xFF, bytes
);
1128 if (bytes
< addrlen
)
1129 mask
[bytes
] = 0xFF << (8 - bits
);
1130 if (bytes
+ 1 < addrlen
)
1131 memset(mask
+ bytes
+ 1, 0x00,
1132 addrlen
- bytes
- 1);
1133 for (a
= 0; a
< addrlen
; a
++)
1134 if ((addr
[a
] & mask
[a
]) != net
[a
])
1139 /* check against hostname glob */
1140 } else if (fnmatch(p
, remotehost
, FNM_CASEFOLD
) != 0)
1144 /* have a group specifier */
1145 if ((p
= strchr(word
, ':')) != NULL
) {
1147 int gsize
, i
, found
;
1150 continue; /* no match for unknown user */
1155 ng
= realloc(groups
, gsize
* sizeof(gid_t
));
1158 "Local resource failure: realloc");
1160 } while (getgrouplist(pw
->pw_name
, pw
->pw_gid
,
1161 groups
, &gsize
) == -1);
1163 for (i
= 0; i
< gsize
; i
++) {
1166 if ((g
= getgrgid(groups
[i
])) == NULL
)
1168 if (fnmatch(p
, g
->gr_name
, 0) == 0) {
1178 /* check against username glob */
1179 if (fnmatch(word
, name
, 0) != 0)
1183 ((strcasecmp(perm
, "allow") == 0) ||
1184 (strcasecmp(perm
, "yes") == 0)))
1186 else if (perm
!= NULL
&&
1187 ((strcasecmp(perm
, "deny") == 0) ||
1188 (strcasecmp(perm
, "no") == 0)))
1192 if (!EMPTYSTR(class) && retclass
!= NULL
)
1193 *retclass
= ftpd_strdup(class);
1202 * Check if user is allowed by /etc/ftpusers
1203 * returns 1 for yes, 0 for no
1205 * NOTE: needs struct passwd *pw setup (for checkuser())
1208 checkaccess(const char *name
)
1211 return (checkuser(_NAME_FTPUSERS
, name
, 1, 0, NULL
));
1215 login_utmp(const char *line
, const char *name
, const char *host
,
1216 struct sockinet
*haddr
)
1218 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1220 (void)gettimeofday(&tv
, NULL
);
1222 #ifdef SUPPORT_UTMPX
1224 (void)memset(&utmpx
, 0, sizeof(utmpx
));
1226 utmpx
.ut_pid
= getpid();
1227 utmpx
.ut_id
[0] = 'f';
1228 utmpx
.ut_id
[1] = 't';
1229 utmpx
.ut_id
[2] = 'p';
1230 utmpx
.ut_id
[3] = '*';
1231 utmpx
.ut_type
= USER_PROCESS
;
1232 (void)strncpy(utmpx
.ut_name
, name
, sizeof(utmpx
.ut_name
));
1233 (void)strncpy(utmpx
.ut_line
, line
, sizeof(utmpx
.ut_line
));
1234 (void)strncpy(utmpx
.ut_host
, host
, sizeof(utmpx
.ut_host
));
1235 (void)memcpy(&utmpx
.ut_ss
, &haddr
->si_su
, haddr
->su_len
);
1236 ftpd_loginx(&utmpx
);
1239 ftpd_logwtmpx(line
, name
, host
, haddr
, 0, USER_PROCESS
);
1243 (void)memset(&utmp
, 0, sizeof(utmp
));
1244 (void)time(&utmp
.ut_time
);
1245 (void)strncpy(utmp
.ut_name
, name
, sizeof(utmp
.ut_name
));
1246 (void)strncpy(utmp
.ut_line
, line
, sizeof(utmp
.ut_line
));
1247 (void)strncpy(utmp
.ut_host
, host
, sizeof(utmp
.ut_host
));
1251 ftpd_logwtmp(line
, name
, host
);
1258 #ifdef SUPPORT_UTMPX
1259 int okwtmpx
= dowtmp
;
1262 int okwtmp
= dowtmp
;
1265 #ifdef SUPPORT_UTMPX
1267 okwtmpx
&= ftpd_logoutx(ttyline
, 0, DEAD_PROCESS
);
1269 ftpd_logwtmpx(ttyline
, "", "", NULL
, 0, DEAD_PROCESS
);
1273 okwtmp
&= ftpd_logout(ttyline
);
1275 ftpd_logwtmp(ttyline
, "", "");
1281 * Terminate login as previous user (if any), resetting state;
1282 * used when USER command is given or login fails.
1291 show_chdir_messages(-1); /* flush chdir cache */
1292 if (pw
!= NULL
&& pw
->pw_passwd
!= NULL
)
1293 memset(pw
->pw_passwd
, 0, strlen(pw
->pw_passwd
));
1300 curclass
.type
= CLASS_REAL
;
1301 (void) seteuid((uid_t
)0);
1303 setusercontext(NULL
, getpwuid(0), 0,
1304 LOGIN_SETPRIORITY
|LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1308 if ((e
= pam_setcred(pamh
, PAM_DELETE_CRED
)) != PAM_SUCCESS
)
1309 syslog(LOG_ERR
, "pam_setcred: %s",
1310 pam_strerror(pamh
, e
));
1311 if ((e
= pam_close_session(pamh
,0)) != PAM_SUCCESS
)
1312 syslog(LOG_ERR
, "pam_close_session: %s",
1313 pam_strerror(pamh
, e
));
1314 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
)
1315 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
1322 pass(const char *passwd
)
1324 do_pass(0, 0, passwd
);
1328 * Perform the passwd confirmation and login.
1330 * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval.
1331 * This is the traditional PASS implementation.
1333 * If pass_checked is non-zero, use pass_rval and ignore passwd.
1334 * This is used by auth_pam() which has already parsed PASS.
1335 * This only applies to curclass.type != CLASS_GUEST.
1338 do_pass(int pass_checked
, int pass_rval
, const char *passwd
)
1341 char root
[MAXPATHLEN
];
1343 login_cap_t
*lc
= NULL
;
1351 if (logged_in
|| askpasswd
== 0) {
1352 reply(503, "Login with USER first.");
1356 if (curclass
.type
!= CLASS_GUEST
) {
1357 /* "ftp" is the only account allowed with no password */
1359 rval
= 1; /* failure below */
1362 if (pass_checked
) { /* password validated in user() */
1367 syslog(LOG_ERR
, "do_pass: USE_PAM shouldn't get here");
1371 #if defined(KERBEROS)
1372 if (klogin(pw
, "", hostname
, (char *)passwd
) == 0) {
1377 #if defined(KERBEROS5)
1378 if (k5login(pw
, "", hostname
, (char *)passwd
) == 0) {
1384 if (skey_haskey(pw
->pw_name
) == 0) {
1388 p
= ftpd_strdup(passwd
);
1389 r
= skey_passcheck(pw
->pw_name
, p
);
1398 rval
= checkpassword(pw
, passwd
);
1405 * If rval > 0, the user failed the authentication check
1406 * above. If rval == 0, either Kerberos or local
1407 * authentication succeeded.
1410 reply(530, "%s", rval
== 2 ? "Password expired." :
1411 "Login incorrect.");
1414 "FTP LOGIN FAILED FROM %s", remoteloghost
);
1415 syslog(LOG_AUTHPRIV
| LOG_NOTICE
,
1416 "FTP LOGIN FAILED FROM %s, %s",
1417 remoteloghost
, curname
);
1420 if (login_attempts
++ >= 5) {
1422 "repeated login failures from %s",
1430 /* password ok; check if anything else prevents login */
1432 reply(530, "User %s may not use FTP.", pw
->pw_name
);
1434 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
1435 remoteloghost
, pw
->pw_name
);
1439 login_attempts
= 0; /* this time successful */
1440 if (setegid((gid_t
)pw
->pw_gid
) < 0) {
1441 reply(550, "Can't set gid.");
1445 if ((lc
= login_getpwclass(pw
)) != NULL
) {
1447 char remote_ip
[NI_MAXHOST
];
1449 if (getnameinfo((struct sockaddr
*)&his_addr
, his_addr
.su_len
,
1450 remote_ip
, sizeof(remote_ip
) - 1, NULL
, 0,
1453 remote_ip
[sizeof(remote_ip
) - 1] = 0;
1454 if (!auth_hostok(lc
, remotehost
, remote_ip
)) {
1455 syslog(LOG_INFO
|LOG_AUTH
,
1456 "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1458 reply(530, "Permission denied.");
1462 if (!auth_timeok(lc
, time(NULL
))) {
1463 reply(530, "Login not available right now.");
1470 setusercontext(lc
, pw
, 0,
1471 LOGIN_SETLOGIN
|LOGIN_SETGROUP
|LOGIN_SETPRIORITY
|
1472 LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1474 (void) initgroups(pw
->pw_name
, pw
->pw_gid
);
1475 /* cache groups for cmds.c::matchgroup() */
1479 if ((e
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
1480 syslog(LOG_ERR
, "pam_open_session: %s",
1481 pam_strerror(pamh
, e
));
1482 } else if ((e
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
))
1484 syslog(LOG_ERR
, "pam_setcred: %s",
1485 pam_strerror(pamh
, e
));
1489 gidcount
= getgroups(0, NULL
);
1492 gidlist
= malloc(gidcount
* sizeof *gidlist
);
1493 gidcount
= getgroups(gidcount
, gidlist
);
1495 /* open utmp/wtmp before chroot */
1496 login_utmp(ttyline
, pw
->pw_name
, remotehost
, &his_addr
);
1503 if (curclass
.limit
!= -1 && connections
> curclass
.limit
) {
1504 if (! EMPTYSTR(curclass
.limitfile
))
1505 (void)display_file(conffilename(curclass
.limitfile
),
1508 "User %s access denied, connection limit of " LLF
1510 pw
->pw_name
, (LLT
)curclass
.limit
);
1512 "Maximum connection limit of " LLF
1513 " for class %s reached, login refused for %s",
1514 (LLT
)curclass
.limit
, curclass
.classname
, pw
->pw_name
);
1519 switch (curclass
.type
) {
1522 * We MUST do a chdir() after the chroot. Otherwise
1523 * the old current directory will be accessible as "."
1524 * outside the new root!
1527 curclass
.chroot
? curclass
.chroot
:
1530 format_path(homedir
,
1531 curclass
.homedir
? curclass
.homedir
:
1533 if (EMPTYSTR(homedir
))
1535 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1537 "GUEST user %s: can't chroot to %s: %m",
1541 if (chdir(homedir
) < 0) {
1543 "GUEST user %s: can't chdir to %s: %m",
1544 pw
->pw_name
, homedir
);
1546 reply(550, "Can't set guest privileges.");
1552 curclass
.chroot
? curclass
.chroot
:
1554 format_path(homedir
,
1555 curclass
.homedir
? curclass
.homedir
:
1557 if (EMPTYSTR(homedir
))
1559 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1561 "CHROOT user %s: can't chroot to %s: %m",
1565 if (chdir(homedir
) < 0) {
1567 "CHROOT user %s: can't chdir to %s: %m",
1568 pw
->pw_name
, homedir
);
1570 reply(550, "Can't change root.");
1575 /* only chroot REAL if explicitly requested */
1576 if (! EMPTYSTR(curclass
.chroot
)) {
1577 format_path(root
, curclass
.chroot
);
1578 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1580 "REAL user %s: can't chroot to %s: %m",
1585 format_path(homedir
,
1586 curclass
.homedir
? curclass
.homedir
:
1588 if (EMPTYSTR(homedir
) || chdir(homedir
) < 0) {
1589 if (chdir("/") < 0) {
1591 "REAL user %s: can't chdir to %s: %m",
1593 !EMPTYSTR(homedir
) ? homedir
: "/");
1595 "User %s: can't change directory to %s.",
1597 !EMPTYSTR(homedir
) ? homedir
: "/");
1601 "No directory! Logging in with home=/");
1609 #if !defined(__minix)
1610 setlogin(pw
->pw_name
);
1612 #endif /* !defined(__minix) */
1614 (curclass
.type
!= CLASS_REAL
&&
1615 ntohs(ctrl_addr
.su_port
) > IPPORT_RESERVED
+ 1)) {
1617 if (setgid((gid_t
)pw
->pw_gid
) < 0) {
1618 reply(550, "Can't set gid.");
1621 if (setuid((uid_t
)pw
->pw_uid
) < 0) {
1622 reply(550, "Can't set uid.");
1626 if (seteuid((uid_t
)pw
->pw_uid
) < 0) {
1627 reply(550, "Can't set uid.");
1631 setenv("HOME", homedir
, 1);
1633 if (curclass
.type
== CLASS_GUEST
&& passwd
[0] == '-')
1637 * Display a login message, if it exists.
1638 * N.B. reply(230,) must follow the message.
1640 if (! EMPTYSTR(curclass
.motd
))
1641 (void)display_file(conffilename(curclass
.motd
), 230);
1642 show_chdir_messages(230);
1643 if (curclass
.type
== CLASS_GUEST
) {
1646 reply(230, "Guest login ok, access restrictions apply.");
1647 #if defined(HAVE_SETPROCTITLE)
1648 snprintf(proctitle
, sizeof(proctitle
),
1649 "%s: anonymous/%s", remotehost
, passwd
);
1650 setproctitle("%s", proctitle
);
1651 #endif /* defined(HAVE_SETPROCTITLE) */
1654 "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1655 remoteloghost
, passwd
,
1656 curclass
.classname
, CURCLASSTYPE
);
1657 /* store guest password reply into pw_passwd */
1658 REASSIGN(pw
->pw_passwd
, ftpd_strdup(passwd
));
1659 for (p
= pw
->pw_passwd
; *p
; p
++)
1660 if (!isgraph((unsigned char)*p
))
1663 reply(230, "User %s logged in.", pw
->pw_name
);
1664 #if defined(HAVE_SETPROCTITLE)
1665 snprintf(proctitle
, sizeof(proctitle
),
1666 "%s: %s", remotehost
, pw
->pw_name
);
1667 setproctitle("%s", proctitle
);
1668 #endif /* defined(HAVE_SETPROCTITLE) */
1671 "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1672 remoteloghost
, pw
->pw_name
,
1673 curclass
.classname
, CURCLASSTYPE
);
1675 (void) umask(curclass
.umask
);
1685 /* Forget all about it... */
1690 retrieve(const char *argv
[], const char *name
)
1694 int (*closefunc
)(FILE *) = NULL
;
1695 int dolog
, sendrv
, closerv
, stderrfd
, isconversion
, isdata
, isls
;
1696 struct timeval start
, finish
, td
, *tdp
;
1697 #if !defined(__minix)
1698 struct rusage rusage_before
, rusage_after
;
1699 #endif /* !defined(__minix) */
1700 const char *dispname
;
1703 sendrv
= closerv
= stderrfd
= -1;
1704 isconversion
= isdata
= isls
= dolog
= 0;
1709 if (argv
== NULL
) { /* if not running a command ... */
1712 fin
= fopen(name
, "r");
1714 if (fin
== NULL
) /* doesn't exist?; try a conversion */
1715 argv
= do_conversion(name
);
1718 syslog(LOG_DEBUG
, "get command: '%s' on '%s'",
1723 char temp
[MAXPATHLEN
];
1725 if (strcmp(argv
[0], INTERNAL_LS
) == 0) {
1729 (void)snprintf(temp
, sizeof(temp
), "%s", TMPFILE
);
1730 stderrfd
= mkstemp(temp
);
1735 fin
= ftpd_popen(argv
, "r", stderrfd
);
1736 closefunc
= ftpd_pclose
;
1738 st
.st_blksize
= BUFSIZ
;
1742 perror_reply(550, dispname
);
1744 logxfer("get", -1, name
, NULL
, NULL
,
1747 goto cleanupretrieve
;
1751 && (fstat(fileno(fin
), &st
) < 0 || !S_ISREG(st
.st_mode
))) {
1752 error
= "Not a plain file";
1753 reply(550, "%s: %s.", dispname
, error
);
1756 if (restart_point
) {
1757 if (type
== TYPE_A
) {
1761 for (i
= 0; i
< restart_point
; i
++) {
1762 if ((c
=getc(fin
)) == EOF
) {
1763 error
= strerror(errno
);
1764 perror_reply(550, dispname
);
1770 } else if (lseek(fileno(fin
), restart_point
, SEEK_SET
) < 0) {
1771 error
= strerror(errno
);
1772 perror_reply(550, dispname
);
1776 dout
= dataconn(dispname
, st
.st_size
, "w");
1780 #if !defined(__minix)
1781 (void)getrusage(RUSAGE_SELF
, &rusage_before
);
1782 #endif /* !defined(__minix) */
1783 (void)gettimeofday(&start
, NULL
);
1784 sendrv
= send_data(fin
, dout
, &st
, isdata
);
1785 (void)gettimeofday(&finish
, NULL
);
1786 #if !defined(__minix)
1787 (void)getrusage(RUSAGE_SELF
, &rusage_after
);
1788 #endif /* !defined(__minix) */
1789 closedataconn(dout
); /* close now to affect timing stats */
1790 timersub(&finish
, &start
, &td
);
1794 logxfer("get", byte_count
, name
, NULL
, tdp
, error
);
1795 #if !defined(__minix)
1797 logrusage(&rusage_before
, &rusage_after
);
1798 #endif /* !defined(__minix) */
1800 closerv
= (*closefunc
)(fin
);
1805 if (!isls
&& argv
!= NULL
&& closerv
!= 0) {
1807 "Command returned an exit status of %d",
1811 "retrieve command: '%s' returned %d",
1814 if (!isls
&& argv
!= NULL
&& stderrfd
!= -1 &&
1815 (fstat(stderrfd
, &sb
) == 0) && sb
.st_size
> 0 &&
1816 ((errf
= fdopen(stderrfd
, "r")) != NULL
)) {
1817 char *cp
, line
[LINE_MAX
];
1819 reply(-226, "Command error messages:");
1821 while (fgets(line
, sizeof(line
), errf
) != NULL
) {
1822 if ((cp
= strchr(line
, '\n')) != NULL
)
1824 reply(0, " %s", line
);
1826 (void) fflush(stdout
);
1827 (void) fclose(errf
);
1828 /* a reply(226,) must follow */
1830 reply(226, "Transfer complete.");
1834 (void)close(stderrfd
);
1840 store(const char *name
, const char *fmode
, int unique
)
1844 int (*closefunc
)(FILE *);
1845 struct timeval start
, finish
, td
, *tdp
;
1846 const char *desc
, *error
;
1849 desc
= (*fmode
== 'w') ? "put" : "append";
1851 if (unique
&& stat(name
, &st
) == 0 &&
1852 (name
= gunique(name
)) == NULL
) {
1853 logxfer(desc
, -1, name
, NULL
, NULL
,
1854 "cannot create unique file");
1860 fout
= fopen(name
, fmode
);
1864 perror_reply(553, name
);
1865 logxfer(desc
, -1, name
, NULL
, NULL
, strerror(errno
));
1869 if (restart_point
) {
1870 if (type
== TYPE_A
) {
1874 for (i
= 0; i
< restart_point
; i
++) {
1875 if ((c
=getc(fout
)) == EOF
) {
1876 error
= strerror(errno
);
1877 perror_reply(550, name
);
1884 * We must do this seek to "current" position
1885 * because we are changing from reading to
1888 if (fseek(fout
, 0L, SEEK_CUR
) < 0) {
1889 error
= strerror(errno
);
1890 perror_reply(550, name
);
1893 } else if (lseek(fileno(fout
), restart_point
, SEEK_SET
) < 0) {
1894 error
= strerror(errno
);
1895 perror_reply(550, name
);
1899 din
= dataconn(name
, (off_t
)-1, "r");
1902 (void)gettimeofday(&start
, NULL
);
1903 if (receive_data(din
, fout
) == 0) {
1905 reply(226, "Transfer complete (unique file name:%s).",
1908 reply(226, "Transfer complete.");
1910 (void)gettimeofday(&finish
, NULL
);
1911 closedataconn(din
); /* close now to affect timing stats */
1912 timersub(&finish
, &start
, &td
);
1915 logxfer(desc
, byte_count
, name
, NULL
, tdp
, error
);
1922 getdatasock(const char *fmode
)
1924 int on
, s
, t
, tries
;
1929 return (fdopen(data
, fmode
));
1931 (void) seteuid((uid_t
)0);
1932 s
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
1935 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
1936 (char *) &on
, sizeof(on
)) < 0)
1938 if (setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
1939 (char *) &on
, sizeof(on
)) < 0)
1941 /* anchor socket to avoid multi-homing problems */
1942 data_source
= ctrl_addr
;
1944 * By default source port for PORT connctions is
1945 * ctrlport-1 (see RFC959 section 5.2).
1946 * However, if privs have been dropped and that
1947 * would be < IPPORT_RESERVED, use a random port
1953 port
= ntohs(ctrl_addr
.su_port
) - 1;
1954 if (dropprivs
&& port
< IPPORT_RESERVED
)
1955 port
= 0; /* use random port */
1956 data_source
.su_port
= htons(port
);
1958 for (tries
= 1; ; tries
++) {
1959 if (bind(s
, (struct sockaddr
*)&data_source
.si_su
,
1960 data_source
.su_len
) >= 0)
1962 if (errno
!= EADDRINUSE
|| tries
> 10)
1967 (void) seteuid((uid_t
)pw
->pw_uid
);
1969 if (!mapped
&& ctrl_addr
.su_family
== AF_INET
) {
1970 on
= IPTOS_THROUGHPUT
;
1971 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *)&on
,
1973 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
1976 return (fdopen(s
, fmode
));
1978 /* Return the real value of errno (close may change it) */
1981 (void) seteuid((uid_t
)pw
->pw_uid
);
1988 dataconn(const char *name
, off_t size
, const char *fmode
)
1992 int retry
, tos
, keepalive
, conerrno
;
1996 if (size
!= (off_t
) -1)
1997 (void)snprintf(sizebuf
, sizeof(sizebuf
), " (" LLF
" byte%s)",
1998 (LLT
)size
, PLURAL(size
));
2002 struct sockinet from
;
2004 socklen_t fromlen
= sizeof(from
.su_len
);
2006 (void) alarm(curclass
.timeout
);
2007 s
= accept(pdata
, (struct sockaddr
*)&from
.si_su
, &fromlen
);
2010 reply(425, "Can't open data connection.");
2011 (void) close(pdata
);
2015 (void) close(pdata
);
2017 #if !defined(__minix)
2018 switch (from
.su_family
) {
2022 tos
= IPTOS_THROUGHPUT
;
2023 (void) setsockopt(s
, IPPROTO_IP
, IP_TOS
,
2024 (char *)&tos
, sizeof(int));
2029 #endif /* !defined(__minix) */
2030 /* Set keepalives on the socket to detect dropped conns. */
2033 (void) setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
2034 (char *)&keepalive
, sizeof(int));
2036 reply(150, "Opening %s mode data connection for '%s'%s.",
2037 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2038 return (fdopen(pdata
, fmode
));
2041 reply(125, "Using existing data connection for '%s'%s.",
2044 return (fdopen(data
, fmode
));
2047 data_dest
= his_addr
;
2049 retry
= conerrno
= 0;
2051 file
= getdatasock(fmode
);
2053 char hbuf
[NI_MAXHOST
];
2054 char pbuf
[NI_MAXSERV
];
2056 if (getnameinfo((struct sockaddr
*)&data_source
.si_su
,
2057 data_source
.su_len
, hbuf
, sizeof(hbuf
), pbuf
,
2058 sizeof(pbuf
), NI_NUMERICHOST
| NI_NUMERICSERV
))
2059 strlcpy(hbuf
, "?", sizeof(hbuf
));
2060 reply(425, "Can't create data socket (%s,%s): %s.",
2061 hbuf
, pbuf
, strerror(errno
));
2064 data
= fileno(file
);
2066 if (connect(data
, (struct sockaddr
*)&data_dest
.si_su
,
2067 data_dest
.su_len
) == 0)
2070 (void) fclose(file
);
2073 if (conerrno
== EADDRINUSE
) {
2074 sleep((unsigned) swaitint
);
2079 } while (retry
<= swaitmax
);
2080 if (conerrno
!= 0) {
2081 perror_reply(425, "Can't build data connection");
2084 reply(150, "Opening %s mode data connection for '%s'%s.",
2085 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2090 closedataconn(FILE *fd
)
2103 write_data(int fd
, char *buf
, size_t size
, off_t
*bufrem
,
2104 struct timeval
*then
, int isdata
)
2106 struct timeval now
, td
;
2111 if (curclass
.writesize
) {
2112 if (curclass
.writesize
< c
)
2113 c
= curclass
.writesize
;
2115 if (curclass
.rateget
) {
2119 (void) alarm(curclass
.timeout
);
2120 c
= write(fd
, buf
, c
);
2127 total_data_out
+= c
;
2130 total_bytes_out
+= c
;
2132 if (curclass
.rateget
) {
2135 (void)gettimeofday(&now
, NULL
);
2136 timersub(&now
, then
, &td
);
2137 if (td
.tv_sec
== 0) {
2138 usleep(1000000 - td
.tv_usec
);
2139 (void)gettimeofday(then
, NULL
);
2142 *bufrem
= curclass
.rateget
;
2149 static enum send_status
2150 send_data_with_read(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2152 struct timeval then
;
2158 if (curclass
.readsize
> 0)
2159 readsize
= curclass
.readsize
;
2161 readsize
= st
->st_blksize
;
2162 if ((buf
= malloc(readsize
)) == NULL
) {
2163 perror_reply(451, "Local resource failure: malloc");
2164 return (SS_NO_TRANSFER
);
2167 if (curclass
.rateget
) {
2168 bufrem
= curclass
.rateget
;
2169 (void)gettimeofday(&then
, NULL
);
2173 (void) alarm(curclass
.timeout
);
2174 c
= read(filefd
, buf
, readsize
);
2178 error
= SS_FILE_ERROR
;
2179 else if (write_data(netfd
, buf
, c
, &bufrem
, &then
, isdata
))
2180 error
= SS_DATA_ERROR
;
2181 else if (urgflag
&& handleoobcmd())
2191 static enum send_status
2192 send_data_with_mmap(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2194 struct timeval then
;
2195 off_t bufrem
, filesize
, off
, origoff
;
2196 ssize_t mapsize
, winsize
;
2197 int error
, sendbufsize
, sendlowat
;
2201 if (curclass
.sendbufsize
) {
2202 sendbufsize
= curclass
.sendbufsize
;
2203 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDBUF
,
2204 &sendbufsize
, sizeof(int)) == -1)
2205 syslog(LOG_WARNING
, "setsockopt(SO_SNDBUF, %d): %m",
2209 if (curclass
.sendlowat
) {
2210 sendlowat
= curclass
.sendlowat
;
2211 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDLOWAT
,
2212 &sendlowat
, sizeof(int)) == -1)
2213 syslog(LOG_WARNING
, "setsockopt(SO_SNDLOWAT, %d): %m",
2217 winsize
= curclass
.mmapsize
;
2218 filesize
= st
->st_size
;
2220 syslog(LOG_INFO
, "mmapsize = " LLF
", writesize = " LLF
,
2221 (LLT
)winsize
, (LLT
)curclass
.writesize
);
2225 off
= lseek(filefd
, (off_t
)0, SEEK_CUR
);
2230 if (curclass
.rateget
) {
2231 bufrem
= curclass
.rateget
;
2232 (void)gettimeofday(&then
, NULL
);
2236 mapsize
= MIN(filesize
- off
, winsize
);
2239 win
= mmap(NULL
, mapsize
, PROT_READ
,
2240 MAP_FILE
|MAP_SHARED
, filefd
, off
);
2241 if (win
== MAP_FAILED
) {
2244 return (SS_FILE_ERROR
);
2247 (void) madvise(win
, mapsize
, MADV_SEQUENTIAL
);
2249 error
= write_data(netfd
, win
, mapsize
, &bufrem
, &then
,
2252 (void) madvise(win
, mapsize
, MADV_DONTNEED
);
2254 munmap(win
, mapsize
);
2255 if (urgflag
&& handleoobcmd())
2256 return (SS_ABORTED
);
2258 return (SS_DATA_ERROR
);
2261 return (SS_SUCCESS
);
2264 return (send_data_with_read(filefd
, netfd
, st
, isdata
));
2268 * Transfer the contents of "instr" to "outstr" peer using the appropriate
2269 * encapsulation of the data subject to Mode, Structure, and Type.
2271 * NB: Form isn't handled.
2274 send_data(FILE *instr
, FILE *outstr
, const struct stat
*st
, int isdata
)
2276 int c
, filefd
, netfd
, rval
;
2285 /* XXXLUKEM: rate limit ascii send (get) */
2286 (void) alarm(curclass
.timeout
);
2287 while ((c
= getc(instr
)) != EOF
) {
2288 if (urgflag
&& handleoobcmd())
2289 goto cleanup_send_data
;
2294 (void) putc('\r', outstr
);
2302 (void) putc(c
, outstr
);
2309 if ((byte_count
% 4096) == 0)
2310 (void) alarm(curclass
.timeout
);
2319 goto cleanup_send_data
;
2323 filefd
= fileno(instr
);
2324 netfd
= fileno(outstr
);
2325 switch (send_data_with_mmap(filefd
, netfd
, st
, isdata
)) {
2331 case SS_NO_TRANSFER
:
2332 goto cleanup_send_data
;
2341 goto cleanup_send_data
;
2344 reply(550, "Unimplemented TYPE %d in send_data", type
);
2345 goto cleanup_send_data
;
2350 perror_reply(426, "Data connection");
2351 goto cleanup_send_data
;
2355 perror_reply(551, "Error on input file");
2356 goto cleanup_send_data
;
2372 * Transfer data from peer to "outstr" using the appropriate encapulation of
2373 * the data subject to Mode, Structure, and Type.
2375 * N.B.: Form isn't handled.
2378 receive_data(FILE *instr
, FILE *outstr
)
2380 int c
, netfd
, filefd
, rval
;
2381 int volatile bare_lfs
;
2385 struct sigaction sa
, sa_saved
;
2388 memset(&sa
, 0, sizeof(sa
));
2389 sigfillset(&sa
.sa_mask
);
2390 sa
.sa_flags
= SA_RESTART
;
2391 sa
.sa_handler
= lostconn
;
2392 (void) sigaction(SIGALRM
, &sa
, &sa_saved
);
2401 #define FILESIZECHECK(x) \
2403 if (curclass.maxfilesize != -1 && \
2404 (x) > curclass.maxfilesize) { \
2414 netfd
= fileno(instr
);
2415 filefd
= fileno(outstr
);
2416 (void) alarm(curclass
.timeout
);
2417 if (curclass
.readsize
)
2418 readsize
= curclass
.readsize
;
2419 else if (fstat(filefd
, &st
) != -1)
2420 readsize
= (ssize_t
)st
.st_blksize
;
2423 if ((buf
= malloc(readsize
)) == NULL
) {
2424 perror_reply(451, "Local resource failure: malloc");
2425 goto cleanup_recv_data
;
2427 if (curclass
.rateput
) {
2430 struct timeval then
, now
, td
;
2433 (void)gettimeofday(&then
, NULL
);
2435 for (bufrem
= curclass
.rateput
; bufrem
> 0; ) {
2436 if ((c
= read(netfd
, buf
,
2437 MIN(readsize
, bufrem
))) <= 0)
2439 if (urgflag
&& handleoobcmd())
2440 goto cleanup_recv_data
;
2441 FILESIZECHECK(byte_count
+ c
);
2442 if ((d
= write(filefd
, buf
, c
)) != c
)
2444 (void) alarm(curclass
.timeout
);
2449 total_bytes_in
+= c
;
2452 (void)gettimeofday(&now
, NULL
);
2453 timersub(&now
, &then
, &td
);
2455 usleep(1000000 - td
.tv_usec
);
2458 while ((c
= read(netfd
, buf
, readsize
)) > 0) {
2459 if (urgflag
&& handleoobcmd())
2460 goto cleanup_recv_data
;
2461 FILESIZECHECK(byte_count
+ c
);
2462 if (write(filefd
, buf
, c
) != c
)
2464 (void) alarm(curclass
.timeout
);
2468 total_bytes_in
+= c
;
2476 goto cleanup_recv_data
;
2479 reply(553, "TYPE E not implemented.");
2480 goto cleanup_recv_data
;
2483 (void) alarm(curclass
.timeout
);
2484 /* XXXLUKEM: rate limit ascii receive (put) */
2485 while ((c
= getc(instr
)) != EOF
) {
2486 if (urgflag
&& handleoobcmd())
2487 goto cleanup_recv_data
;
2493 if ((byte_count
% 4096) == 0)
2494 (void) alarm(curclass
.timeout
);
2500 if ((c
= getc(instr
)) != '\n') {
2506 if ((byte_count
% 4096) == 0)
2507 (void) alarm(curclass
.timeout
);
2509 FILESIZECHECK(byteswritten
);
2510 (void) putc ('\r', outstr
);
2511 if (c
== '\0' || c
== EOF
)
2516 FILESIZECHECK(byteswritten
);
2517 (void) putc(c
, outstr
);
2528 "WARNING! %d bare linefeeds received in ASCII mode",
2530 reply(0, "File may not have transferred correctly.");
2533 goto cleanup_recv_data
;
2536 reply(550, "Unimplemented TYPE %d in receive_data", type
);
2537 goto cleanup_recv_data
;
2539 #undef FILESIZECHECK
2543 perror_reply(426, "Data Connection");
2544 goto cleanup_recv_data
;
2548 perror_reply(452, "Error writing file");
2549 goto cleanup_recv_data
;
2553 (void) sigaction(SIGALRM
, &sa_saved
, NULL
);
2568 struct sockinet
*su
= NULL
;
2569 static char hbuf
[NI_MAXHOST
], sbuf
[NI_MAXSERV
];
2570 unsigned char *a
, *p
;
2572 off_t otbi
, otbo
, otb
;
2576 reply(-211, "%s FTP server status:", hostname
);
2577 reply(0, "Version: %s", EMPTYSTR(version
) ? "<suppressed>" : version
);
2579 if (!getnameinfo((struct sockaddr
*)&his_addr
.si_su
, his_addr
.su_len
,
2580 hbuf
, sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
)
2581 && strcmp(remotehost
, hbuf
) != 0)
2582 reply(0, "Connected to %s (%s)", remotehost
, hbuf
);
2584 reply(0, "Connected to %s", remotehost
);
2587 if (curclass
.type
== CLASS_GUEST
)
2588 reply(0, "Logged in anonymously");
2590 reply(0, "Logged in as %s%s", pw
->pw_name
,
2591 curclass
.type
== CLASS_CHROOT
? " (chroot)" : "");
2592 } else if (askpasswd
)
2593 reply(0, "Waiting for password");
2595 reply(0, "Waiting for user name");
2596 cprintf(stdout
, " TYPE: %s", typenames
[type
]);
2597 if (type
== TYPE_A
|| type
== TYPE_E
)
2598 cprintf(stdout
, ", FORM: %s", formnames
[form
]);
2599 if (type
== TYPE_L
) {
2601 cprintf(stdout
, " %d", NBBY
);
2603 /* XXX: `bytesize' needs to be defined in this case */
2604 cprintf(stdout
, " %d", bytesize
);
2607 cprintf(stdout
, "; STRUcture: %s; transfer MODE: %s\r\n",
2608 strunames
[stru
], modenames
[mode
]);
2611 reply(0, "Data connection open");
2613 } else if (pdata
!= -1) {
2614 reply(0, "in Passive mode");
2615 if (curclass
.advertise
.su_len
!= 0)
2616 su
= &curclass
.advertise
;
2621 } else if (usedefault
== 0) {
2622 su
= (struct sockinet
*)&data_dest
;
2625 reply(0, "EPSV only mode (EPSV ALL)");
2630 if (su
->su_family
== AF_INET
) {
2631 a
= (unsigned char *) &su
->su_addr
;
2632 p
= (unsigned char *) &su
->su_port
;
2633 #define UC(b) (((int) b) & 0xff)
2634 reply(0, "%s (%d,%d,%d,%d,%d,%d)",
2635 ispassive
? "PASV" : "PORT" ,
2636 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
2637 UC(p
[0]), UC(p
[1]));
2645 switch (su
->su_family
) {
2647 a
= (unsigned char *) &su
->su_addr
;
2648 p
= (unsigned char *) &su
->su_port
;
2649 alen
= sizeof(su
->su_addr
);
2654 a
= (unsigned char *) &su
->su_6addr
;
2655 p
= (unsigned char *) &su
->su_port
;
2656 alen
= sizeof(su
->su_6addr
);
2665 cprintf(stdout
, " %s (%d,%d",
2666 ispassive
? "LPSV" : "LPRT", af
, alen
);
2667 for (i
= 0; i
< alen
; i
++)
2668 cprintf(stdout
, ",%d", UC(a
[i
]));
2669 cprintf(stdout
, ",%d,%d,%d)\r\n",
2670 2, UC(p
[0]), UC(p
[1]));
2677 af
= af2epsvproto(su
->su_family
);
2680 struct sockinet tmp
;
2684 if (tmp
.su_family
== AF_INET6
)
2685 tmp
.su_scope_id
= 0;
2687 if (getnameinfo((struct sockaddr
*)&tmp
.si_su
,
2688 tmp
.su_len
, hbuf
, sizeof(hbuf
), sbuf
, sizeof(sbuf
),
2689 NI_NUMERICHOST
| NI_NUMERICSERV
) == 0)
2690 reply(0, "%s (|%d|%s|%s|)",
2691 ispassive
? "EPSV" : "EPRT",
2695 reply(0, "No data connection");
2699 "Data sent: " LLF
" byte%s in " LLF
" file%s",
2700 (LLT
)total_data_out
, PLURAL(total_data_out
),
2701 (LLT
)total_files_out
, PLURAL(total_files_out
));
2703 "Data received: " LLF
" byte%s in " LLF
" file%s",
2704 (LLT
)total_data_in
, PLURAL(total_data_in
),
2705 (LLT
)total_files_in
, PLURAL(total_files_in
));
2707 "Total data: " LLF
" byte%s in " LLF
" file%s",
2708 (LLT
)total_data
, PLURAL(total_data
),
2709 (LLT
)total_files
, PLURAL(total_files
));
2711 otbi
= total_bytes_in
;
2712 otbo
= total_bytes_out
;
2714 reply(0, "Traffic sent: " LLF
" byte%s in " LLF
" transfer%s",
2715 (LLT
)otbo
, PLURAL(otbo
),
2716 (LLT
)total_xfers_out
, PLURAL(total_xfers_out
));
2717 reply(0, "Traffic received: " LLF
" byte%s in " LLF
" transfer%s",
2718 (LLT
)otbi
, PLURAL(otbi
),
2719 (LLT
)total_xfers_in
, PLURAL(total_xfers_in
));
2720 reply(0, "Total traffic: " LLF
" byte%s in " LLF
" transfer%s",
2721 (LLT
)otb
, PLURAL(otb
),
2722 (LLT
)total_xfers
, PLURAL(total_xfers
));
2724 if (logged_in
&& !CURCLASS_FLAGS_ISSET(private)) {
2728 reply(0, "Class: %s, type: %s",
2729 curclass
.classname
, CURCLASSTYPE
);
2730 reply(0, "Check PORT/LPRT commands: %sabled",
2731 CURCLASS_FLAGS_ISSET(checkportcmd
) ? "en" : "dis");
2732 if (! EMPTYSTR(curclass
.display
))
2733 reply(0, "Display file: %s", curclass
.display
);
2734 if (! EMPTYSTR(curclass
.notify
))
2735 reply(0, "Notify fileglob: %s", curclass
.notify
);
2736 reply(0, "Idle timeout: " LLF
", maximum timeout: " LLF
,
2737 (LLT
)curclass
.timeout
, (LLT
)curclass
.maxtimeout
);
2738 reply(0, "Current connections: %d", connections
);
2739 if (curclass
.limit
== -1)
2740 reply(0, "Maximum connections: unlimited");
2742 reply(0, "Maximum connections: " LLF
,
2743 (LLT
)curclass
.limit
);
2744 if (curclass
.limitfile
)
2745 reply(0, "Connection limit exceeded message file: %s",
2746 conffilename(curclass
.limitfile
));
2747 if (! EMPTYSTR(curclass
.chroot
))
2748 reply(0, "Chroot format: %s", curclass
.chroot
);
2749 reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2750 CURCLASS_FLAGS_ISSET(denyquick
) ? "en" : "dis");
2751 if (! EMPTYSTR(curclass
.homedir
))
2752 reply(0, "Homedir format: %s", curclass
.homedir
);
2753 if (curclass
.maxfilesize
== -1)
2754 reply(0, "Maximum file size: unlimited");
2756 reply(0, "Maximum file size: " LLF
,
2757 (LLT
)curclass
.maxfilesize
);
2758 if (! EMPTYSTR(curclass
.motd
))
2759 reply(0, "MotD file: %s", conffilename(curclass
.motd
));
2761 "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2762 CURCLASS_FLAGS_ISSET(modify
) ? "en" : "dis");
2763 reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2764 CURCLASS_FLAGS_ISSET(upload
) ? "en" : "dis");
2765 reply(0, "Sanitize file names: %sabled",
2766 CURCLASS_FLAGS_ISSET(sanenames
) ? "en" : "dis");
2767 reply(0, "PASV/LPSV/EPSV connections: %sabled",
2768 CURCLASS_FLAGS_ISSET(passive
) ? "en" : "dis");
2769 if (curclass
.advertise
.su_len
!= 0) {
2770 char buf
[50]; /* big enough for IPv6 address */
2773 bp
= inet_ntop(curclass
.advertise
.su_family
,
2774 (void *)&curclass
.advertise
.su_addr
,
2777 reply(0, "PASV advertise address: %s", bp
);
2779 if (curclass
.portmin
&& curclass
.portmax
)
2780 reply(0, "PASV port range: " LLF
" - " LLF
,
2781 (LLT
)curclass
.portmin
, (LLT
)curclass
.portmax
);
2782 if (curclass
.rateget
)
2783 reply(0, "Rate get limit: " LLF
" bytes/sec",
2784 (LLT
)curclass
.rateget
);
2786 reply(0, "Rate get limit: disabled");
2787 if (curclass
.rateput
)
2788 reply(0, "Rate put limit: " LLF
" bytes/sec",
2789 (LLT
)curclass
.rateput
);
2791 reply(0, "Rate put limit: disabled");
2792 if (curclass
.mmapsize
)
2793 reply(0, "Mmap size: " LLF
, (LLT
)curclass
.mmapsize
);
2795 reply(0, "Mmap size: disabled");
2796 if (curclass
.readsize
)
2797 reply(0, "Read size: " LLF
, (LLT
)curclass
.readsize
);
2799 reply(0, "Read size: default");
2800 if (curclass
.writesize
)
2801 reply(0, "Write size: " LLF
, (LLT
)curclass
.writesize
);
2803 reply(0, "Write size: default");
2804 if (curclass
.recvbufsize
)
2805 reply(0, "Receive buffer size: " LLF
,
2806 (LLT
)curclass
.recvbufsize
);
2808 reply(0, "Receive buffer size: default");
2809 if (curclass
.sendbufsize
)
2810 reply(0, "Send buffer size: " LLF
,
2811 (LLT
)curclass
.sendbufsize
);
2813 reply(0, "Send buffer size: default");
2814 if (curclass
.sendlowat
)
2815 reply(0, "Send low water mark: " LLF
,
2816 (LLT
)curclass
.sendlowat
);
2818 reply(0, "Send low water mark: default");
2819 reply(0, "Umask: %.04o", curclass
.umask
);
2820 for (cp
= curclass
.conversions
; cp
!= NULL
; cp
=cp
->next
) {
2821 if (cp
->suffix
== NULL
|| cp
->types
== NULL
||
2822 cp
->command
== NULL
)
2824 reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2825 cp
->suffix
, cp
->types
, cp
->disable
, cp
->command
);
2829 reply(211, "End of status");
2833 fatal(const char *s
)
2836 reply(451, "Error in server: %s\n", s
);
2837 reply(221, "Closing connection due to server error.");
2844 * depending on the value of n, display fmt with a trailing CRLF and
2846 * n < -1 prefix the message with abs(n) + "-" (initial line)
2847 * n == 0 prefix the message with 4 spaces (middle lines)
2848 * n > 0 prefix the message with n + " " (final line)
2851 reply(int n
, const char *fmt
, ...)
2853 char msg
[MAXPATHLEN
* 2 + 100];
2858 b
= snprintf(msg
, sizeof(msg
), " ");
2860 b
= snprintf(msg
, sizeof(msg
), "%d-", -n
);
2862 b
= snprintf(msg
, sizeof(msg
), "%d ", n
);
2864 vsnprintf(msg
+ b
, sizeof(msg
) - b
, fmt
, ap
);
2866 cprintf(stdout
, "%s\r\n", msg
);
2867 (void)fflush(stdout
);
2869 syslog(LOG_DEBUG
, "<--- %s", msg
);
2873 logremotehost(struct sockinet
*who
)
2876 #if defined(HAVE_SOCKADDR_SNPRINTF)
2880 struct sockaddr
*sa
= (struct sockaddr
*)&who
->si_su
;
2881 if (getnameinfo(sa
, who
->su_len
, remotehost
, sizeof(remotehost
), NULL
,
2883 strlcpy(remotehost
, "?", sizeof(remotehost
));
2884 #if defined(HAVE_SOCKADDR_SNPRINTF)
2885 sockaddr_snprintf(abuf
, sizeof(abuf
), "%a", sa
);
2886 snprintf(remoteloghost
, sizeof(remoteloghost
), "%s(%s)", remotehost
,
2889 strlcpy(remoteloghost
, remotehost
, sizeof(remoteloghost
));
2892 #if defined(HAVE_SETPROCTITLE)
2893 snprintf(proctitle
, sizeof(proctitle
), "%s: connected", remotehost
);
2894 setproctitle("%s", proctitle
);
2895 #endif /* defined(HAVE_SETPROCTITLE) */
2897 syslog(LOG_INFO
, "connection from %s to %s",
2898 remoteloghost
, hostname
);
2902 * Record logout in wtmp file and exit with supplied status.
2903 * NOTE: because this is called from signal handlers it cannot
2904 * use stdio (or call other functions that use stdio).
2907 dologout(int status
)
2910 * Prevent reception of SIGURG from resulting in a resumption
2911 * back to the main program loop.
2917 if (!notickets
&& krbtkfile_env
)
2918 unlink(krbtkfile_env
);
2921 /* beware of flushing buffers after a SIGPIPE */
2922 if (xferlogfd
!= -1)
2935 reply(426, "Transfer aborted. Data connection closed.");
2936 reply(226, "Abort successful");
2937 transflag
= 0; /* flag that the transfer has aborted */
2948 if (file_size
!= (off_t
) -1)
2950 "Status: " LLF
" of " LLF
" byte%s transferred",
2951 (LLT
)byte_count
, (LLT
)file_size
,
2952 PLURAL(byte_count
));
2954 reply(213, "Status: " LLF
" byte%s transferred",
2955 (LLT
)byte_count
, PLURAL(byte_count
));
2959 * Call when urgflag != 0 to handle Out Of Band commands.
2960 * Returns non zero if the OOB command aborted the transfer
2961 * by setting transflag to 0. (c.f., "ABOR").
2972 /* only process if transfer occurring */
2976 ret
= get_line(cp
, sizeof(tmpline
)-1, stdin
);
2978 reply(221, "You could at least say goodbye.");
2980 } else if (ret
== -2) {
2981 /* Ignore truncated command */
2982 /* XXX: abort xfer with "500 command too long", & return 1 ? */
2986 * Manually parse OOB commands, because we can't
2987 * recursively call the yacc parser...
2989 if (strcasecmp(cp
, "ABOR\r\n") == 0) {
2991 } else if (strcasecmp(cp
, "STAT\r\n") == 0) {
2994 /* XXX: error with "500 unknown command" ? */
2996 return (transflag
== 0);
3000 bind_pasv_addr(void)
3002 static int passiveport
;
3005 len
= pasv_addr
.su_len
;
3006 if (curclass
.portmin
== 0 && curclass
.portmax
== 0) {
3007 pasv_addr
.su_port
= 0;
3008 return (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
));
3011 if (passiveport
== 0) {
3013 passiveport
= rand() % (curclass
.portmax
- curclass
.portmin
)
3020 if (port
> curclass
.portmax
)
3021 port
= curclass
.portmin
;
3022 else if (port
== passiveport
) {
3026 pasv_addr
.su_port
= htons(port
);
3027 if (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
) == 0)
3029 if (errno
!= EADDRINUSE
)
3037 * Note: a response of 425 is not mentioned as a possible response to
3038 * the PASV command in RFC959. However, it has been blessed as
3039 * a legitimate response by Jon Postel in a telephone conversation
3040 * with Rick Adams on 25 Jan 89.
3051 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
3052 if (pdata
< 0 || !logged_in
) {
3053 perror_reply(425, "Can't open passive connection");
3056 pasv_addr
= ctrl_addr
;
3058 if (bind_pasv_addr() < 0)
3060 len
= pasv_addr
.su_len
;
3061 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3063 pasv_addr
.su_len
= len
;
3064 if (curclass
.recvbufsize
) {
3065 recvbufsize
= curclass
.recvbufsize
;
3066 if (setsockopt(pdata
, SOL_SOCKET
, SO_RCVBUF
, &recvbufsize
,
3068 syslog(LOG_WARNING
, "setsockopt(SO_RCVBUF, %d): %m",
3071 if (listen(pdata
, 1) < 0)
3073 if (curclass
.advertise
.su_len
!= 0)
3074 a
= (char *) &curclass
.advertise
.su_addr
;
3076 a
= (char *) &pasv_addr
.su_addr
;
3077 p
= (char *) &pasv_addr
.su_port
;
3079 #define UC(b) (((int) b) & 0xff)
3081 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
3082 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
3086 (void) close(pdata
);
3088 perror_reply(425, "Can't open passive connection");
3093 * convert protocol identifier to/from AF
3096 lpsvproto2af(int proto
)
3112 af2lpsvproto(int af
)
3128 epsvproto2af(int proto
)
3144 af2epsvproto(int af
)
3160 * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3161 * 229 Entering Extended Passive Mode (|||port|)
3164 long_passive(const char *cmd
, int pf
)
3170 syslog(LOG_NOTICE
, "long passive but not logged in");
3171 reply(503, "Login with USER first.");
3175 if (pf
!= PF_UNSPEC
&& ctrl_addr
.su_family
!= pf
) {
3177 * XXX: only EPRT/EPSV ready clients will understand this
3179 if (strcmp(cmd
, "EPSV") != 0)
3180 reply(501, "Network protocol mismatch"); /*XXX*/
3182 epsv_protounsupp("Network protocol mismatch");
3189 pdata
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
3191 perror_reply(425, "Can't open passive connection");
3194 pasv_addr
= ctrl_addr
;
3195 if (bind_pasv_addr() < 0)
3197 len
= pasv_addr
.su_len
;
3198 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3200 pasv_addr
.su_len
= len
;
3201 if (listen(pdata
, 1) < 0)
3203 p
= (char *) &pasv_addr
.su_port
;
3205 #define UC(b) (((int) b) & 0xff)
3207 if (strcmp(cmd
, "LPSV") == 0) {
3208 struct sockinet
*advert
;
3210 if (curclass
.advertise
.su_len
!= 0)
3211 advert
= &curclass
.advertise
;
3213 advert
= &pasv_addr
;
3214 switch (advert
->su_family
) {
3216 a
= (char *) &advert
->su_addr
;
3218 "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3219 4, 4, UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3220 2, UC(p
[0]), UC(p
[1]));
3224 a
= (char *) &advert
->su_6addr
;
3226 "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3228 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3229 UC(a
[4]), UC(a
[5]), UC(a
[6]), UC(a
[7]),
3230 UC(a
[8]), UC(a
[9]), UC(a
[10]), UC(a
[11]),
3231 UC(a
[12]), UC(a
[13]), UC(a
[14]), UC(a
[15]),
3232 2, UC(p
[0]), UC(p
[1]));
3237 } else if (strcmp(cmd
, "EPSV") == 0) {
3238 switch (pasv_addr
.su_family
) {
3243 reply(229, "Entering Extended Passive Mode (|||%d|)",
3244 ntohs(pasv_addr
.su_port
));
3248 /* more proper error code? */
3252 (void) close(pdata
);
3254 perror_reply(425, "Can't open passive connection");
3259 extended_port(const char *arg
)
3265 struct addrinfo hints
;
3266 struct addrinfo
*res
= NULL
;
3268 unsigned long proto
;
3270 tmp
= ftpd_strdup(arg
);
3274 memset(result
, 0, sizeof(result
));
3275 for (i
= 0; i
< 3; i
++) {
3276 q
= strchr(p
, delim
);
3277 if (!q
|| *q
!= delim
)
3284 /* some more sanity checks */
3287 (void)strtoul(result
[2], &p
, 10);
3288 if (errno
|| !*result
[2] || *p
)
3292 proto
= strtoul(result
[0], &p
, 10);
3293 if (errno
|| !*result
[0] || *p
)
3296 memset(&hints
, 0, sizeof(hints
));
3297 hints
.ai_family
= epsvproto2af((int)proto
);
3298 if (hints
.ai_family
< 0)
3300 hints
.ai_socktype
= SOCK_STREAM
;
3301 hints
.ai_flags
= AI_NUMERICHOST
;
3302 if (getaddrinfo(result
[1], result
[2], &hints
, &res
))
3306 if (sizeof(data_dest
) < res
->ai_addrlen
)
3308 memcpy(&data_dest
.si_su
, res
->ai_addr
, res
->ai_addrlen
);
3309 data_dest
.su_len
= res
->ai_addrlen
;
3311 if (his_addr
.su_family
== AF_INET6
&&
3312 data_dest
.su_family
== AF_INET6
) {
3313 /* XXX: more sanity checks! */
3314 data_dest
.su_scope_id
= his_addr
.su_scope_id
;
3325 reply(500, "Invalid argument, rejected.");
3334 epsv_protounsupp("Protocol not supported");
3342 * 522 Protocol not supported (proto,...)
3343 * as we assume address family for control and data connections are the same,
3344 * we do not return the list of address families we support - instead, we
3345 * return the address family of the control connection.
3348 epsv_protounsupp(const char *message
)
3352 proto
= af2epsvproto(ctrl_addr
.su_family
);
3354 reply(501, "%s", message
); /* XXX */
3356 reply(522, "%s, use (%d)", message
, proto
);
3360 * Generate unique name for file with basename "local".
3361 * The file named "local" is already known to exist.
3362 * Generates failure reply on error.
3364 * XXX: this function should under go changes similar to
3365 * the mktemp(3)/mkstemp(3) changes.
3368 gunique(const char *local
)
3370 static char new[MAXPATHLEN
];
3375 cp
= strrchr(local
, '/');
3378 if (stat(cp
? local
: ".", &st
) < 0) {
3379 perror_reply(553, cp
? local
: ".");
3384 for (count
= 1; count
< 100; count
++) {
3385 (void)snprintf(new, sizeof(new) - 1, "%s.%d", local
, count
);
3386 if (stat(new, &st
) < 0)
3389 reply(452, "Unique file name cannot be created.");
3394 * Format and send reply containing system error number.
3397 perror_reply(int code
, const char *string
)
3402 reply(code
, "%s: %s.", string
, strerror(errno
));
3406 static char *onefile
[] = {
3412 send_file_list(const char *whichf
)
3417 FILE *volatile dout
;
3418 char **volatile dirlist
;
3421 int volatile simple
;
3422 int volatile freeglob
;
3433 if (strpbrk(whichf
, "~{[*?") != NULL
) {
3434 int flags
= GLOB_BRACE
|GLOB_NOCHECK
|GLOB_TILDE
|GLOB_LIMIT
;
3436 memset(&gl
, 0, sizeof(gl
));
3438 if (glob(whichf
, flags
, 0, &gl
)) {
3439 reply(450, "Not found");
3440 goto cleanup_send_file_list
;
3441 } else if (gl
.gl_pathc
== 0) {
3443 perror_reply(450, whichf
);
3444 goto cleanup_send_file_list
;
3446 dirlist
= gl
.gl_pathv
;
3448 notglob
= ftpd_strdup(whichf
);
3449 onefile
[0] = notglob
;
3453 /* XXX: } for vi sm */
3455 while ((dirname
= *dirlist
++) != NULL
) {
3456 int trailingslash
= 0;
3458 if (stat(dirname
, &st
) < 0) {
3460 * If user typed "ls -l", etc, and the client
3461 * used NLST, do what the user meant.
3463 /* XXX: nuke this support? */
3464 if (dirname
[0] == '-' && *dirlist
== NULL
&&
3466 const char *argv
[] = { INTERNAL_LS
, "", NULL
};
3469 retrieve(argv
, dirname
);
3470 goto cleanup_send_file_list
;
3472 perror_reply(450, whichf
);
3473 goto cleanup_send_file_list
;
3476 if (S_ISREG(st
.st_mode
)) {
3479 * should we follow RFC959 and not work
3480 * for non directories?
3483 dout
= dataconn("file list", (off_t
)-1, "w");
3485 goto cleanup_send_file_list
;
3488 cprintf(dout
, "%s%s\n", dirname
,
3489 type
== TYPE_A
? "\r" : "");
3491 } else if (!S_ISDIR(st
.st_mode
))
3494 if (dirname
[strlen(dirname
) - 1] == '/')
3497 if ((dirp
= opendir(dirname
)) == NULL
)
3500 while ((dir
= readdir(dirp
)) != NULL
) {
3501 char nbuf
[MAXPATHLEN
];
3503 if (urgflag
&& handleoobcmd())
3504 goto cleanup_send_file_list
;
3506 if (ISDOTDIR(dir
->d_name
) || ISDOTDOTDIR(dir
->d_name
))
3509 (void)snprintf(nbuf
, sizeof(nbuf
), "%s%s%s", dirname
,
3510 trailingslash
? "" : "/", dir
->d_name
);
3513 * We have to do a stat to ensure it's
3514 * not a directory or special file.
3518 * should we follow RFC959 and filter out
3519 * non files ? lukem - NO!, or not until
3520 * our ftp client uses MLS{T,D} for completion.
3522 if (simple
|| (stat(nbuf
, &st
) == 0 &&
3523 S_ISREG(st
.st_mode
))) {
3525 dout
= dataconn("file list", (off_t
)-1,
3528 goto cleanup_send_file_list
;
3532 if (nbuf
[0] == '.' && nbuf
[1] == '/')
3534 cprintf(dout
, "%s%s\n", p
,
3535 type
== TYPE_A
? "\r" : "");
3538 (void) closedir(dirp
);
3542 reply(450, "No files found.");
3543 else if (ferror(dout
) != 0)
3544 perror_reply(451, "Data connection");
3546 reply(226, "Transfer complete.");
3548 cleanup_send_file_list
:
3549 closedataconn(dout
);
3561 conffilename(const char *s
)
3563 static char filename
[MAXPATHLEN
];
3566 strlcpy(filename
, s
, sizeof(filename
));
3568 (void)snprintf(filename
, sizeof(filename
), "%s/%s", confdir
,s
);
3574 * if logging > 1, then based on the arguments, syslog a message:
3575 * if bytes != -1 "<command> <file1> = <bytes> bytes"
3576 * else if file2 != NULL "<command> <file1> <file2>"
3577 * else "<command> <file1>"
3578 * if elapsed != NULL, append "in xxx.yyy seconds"
3579 * if error != NULL, append ": " + error
3581 * if doxferlog != 0, bytes != -1, and command is "get", "put",
3582 * or "append", syslog and/or write a wu-ftpd style xferlog entry
3585 logxfer(const char *command
, off_t bytes
, const char *file1
, const char *file2
,
3586 const struct timeval
*elapsed
, const char *error
)
3588 char buf
[MAXPATHLEN
* 2 + 100];
3589 char realfile1
[MAXPATHLEN
], realfile2
[MAXPATHLEN
];
3590 const char *r1
, *r2
;
3595 if (logging
<=1 && !doxferlog
)
3599 if ((r1
= realpath(file1
, realfile1
)) == NULL
)
3602 if ((r2
= realpath(file2
, realfile2
)) == NULL
)
3609 len
= snprintf(buf
, sizeof(buf
), "%s %s", command
, r1
);
3610 if (bytes
!= (off_t
)-1)
3611 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3612 " = " LLF
" byte%s", (LLT
) bytes
, PLURAL(bytes
));
3613 else if (r2
!= NULL
)
3614 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3616 if (elapsed
!= NULL
)
3617 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3618 " in " LLF
".%.03ld seconds",
3619 (LLT
)elapsed
->tv_sec
,
3620 (long)(elapsed
->tv_usec
/ 1000));
3622 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3624 syslog(LOG_INFO
, "%s", buf
);
3628 * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3630 if (!doxferlog
|| bytes
== -1)
3633 if (strcmp(command
, "get") == 0)
3635 else if (strcmp(command
, "put") == 0 || strcmp(command
, "append") == 0)
3641 len
= snprintf(buf
, sizeof(buf
),
3642 "%.24s " LLF
" %s " LLF
" %s %c %s %c %c %s FTP 0 * %c\n",
3645 * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
3646 * the full date. This may be problematic for accurate log parsing,
3647 * given that syslog messages don't contain the full date.
3651 (elapsed
== NULL
? 0 : elapsed
->tv_sec
+ (elapsed
->tv_usec
> 0)),
3655 type
== TYPE_A
? 'a' : 'b',
3656 "_", /* XXX: take conversions into account? */
3659 curclass
.type
== CLASS_GUEST
? 'a' :
3660 curclass
.type
== CLASS_CHROOT
? 'g' :
3661 curclass
.type
== CLASS_REAL
? 'r' : '?',
3663 curclass
.type
== CLASS_GUEST
? pw
->pw_passwd
: pw
->pw_name
,
3664 error
!= NULL
? 'i' : 'c'
3667 if ((doxferlog
& 2) && xferlogfd
!= -1)
3668 write(xferlogfd
, buf
, len
);
3669 if ((doxferlog
& 1)) {
3670 buf
[len
-1] = '\n'; /* strip \n from syslog message */
3671 syslog(LOG_INFO
, "xferlog: %s", buf
);
3675 #if !defined(__minix)
3677 * Log the resource usage.
3679 * XXX: more resource usage to logging?
3682 logrusage(const struct rusage
*rusage_before
,
3683 const struct rusage
*rusage_after
)
3685 struct timeval usrtime
, systime
;
3690 timersub(&rusage_after
->ru_utime
, &rusage_before
->ru_utime
, &usrtime
);
3691 timersub(&rusage_after
->ru_stime
, &rusage_before
->ru_stime
, &systime
);
3692 syslog(LOG_INFO
, LLF
".%.03ldu " LLF
".%.03lds %ld+%ldio %ldpf+%ldw",
3693 (LLT
)usrtime
.tv_sec
, (long)(usrtime
.tv_usec
/ 1000),
3694 (LLT
)systime
.tv_sec
, (long)(systime
.tv_usec
/ 1000),
3695 rusage_after
->ru_inblock
- rusage_before
->ru_inblock
,
3696 rusage_after
->ru_oublock
- rusage_before
->ru_oublock
,
3697 rusage_after
->ru_majflt
- rusage_before
->ru_majflt
,
3698 rusage_after
->ru_nswap
- rusage_before
->ru_nswap
);
3700 #endif /* !defined(__minix) */
3703 * Determine if `password' is valid for user given in `pw'.
3704 * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
3707 checkpassword(const struct passwd
*pwent
, const char *password
)
3711 time_t change
, expire
, now
;
3713 change
= expire
= 0;
3718 orig
= pwent
->pw_passwd
; /* save existing password */
3719 expire
= pwent
->pw_expire
;
3720 change
= pwent
->pw_change
;
3721 if (change
== _PASSWORD_CHGNOW
)
3724 if (orig
[0] == '\0') /* don't allow empty passwords */
3727 new = crypt(password
, orig
); /* encrypt given password */
3728 if (strcmp(new, orig
) != 0) /* compare */
3731 if ((expire
&& now
>= expire
) || (change
&& now
>= change
))
3732 return 2; /* check if expired */
3738 ftpd_strdup(const char *s
)
3740 char *new = strdup(s
);
3743 fatal("Local resource failure: malloc");
3749 * As per fprintf(), but increment total_bytes and total_bytes_out,
3750 * by the appropriate amount.
3753 cprintf(FILE *fd
, const char *fmt
, ...)
3759 b
= vfprintf(fd
, fmt
, ap
);
3762 total_bytes_out
+= b
;
3767 * the following code is stolen from imap-uw PAM authentication module and
3771 const char *uname
; /* user name */
3772 int triedonce
; /* if non-zero, tried before */
3776 auth_conv(int num_msg
, const struct pam_message
**msg
,
3777 struct pam_response
**resp
, void *appdata
)
3781 ftpd_cred_t
*cred
= (ftpd_cred_t
*) appdata
;
3782 struct pam_response
*myreply
;
3783 char pbuf
[FTP_BUFLEN
];
3785 if (num_msg
<= 0 || num_msg
> PAM_MAX_NUM_MSG
)
3786 return (PAM_CONV_ERR
);
3787 myreply
= calloc(num_msg
, sizeof *myreply
);
3788 if (myreply
== NULL
)
3791 for (i
= 0; i
< num_msg
; i
++) {
3792 myreply
[i
].resp_retcode
= 0;
3793 myreply
[i
].resp
= NULL
;
3794 switch (msg
[i
]->msg_style
) {
3795 case PAM_PROMPT_ECHO_ON
: /* user */
3796 myreply
[i
].resp
= ftpd_strdup(cred
->uname
);
3797 /* PAM frees resp. */
3799 case PAM_PROMPT_ECHO_OFF
: /* authtok (password) */
3801 * Only send a single 331 reply and
3802 * then expect a PASS.
3804 if (cred
->triedonce
) {
3806 "auth_conv: already performed PAM_PROMPT_ECHO_OFF");
3810 if (msg
[i
]->msg
[0] == '\0') {
3811 (void)strlcpy(pbuf
, "password", sizeof(pbuf
));
3813 /* Uncapitalize msg */
3814 (void)strlcpy(pbuf
, msg
[i
]->msg
, sizeof(pbuf
));
3815 if (isupper((unsigned char)pbuf
[0]))
3817 (unsigned char)pbuf
[0]);
3818 /* Remove trailing ':' and whitespace */
3821 if (isspace((unsigned char)pbuf
[n
]) ||
3828 /* Send reply, wait for a response. */
3829 reply(331, "User %s accepted, provide %s.",
3831 (void) alarm(curclass
.timeout
);
3832 ret
= get_line(pbuf
, sizeof(pbuf
)-1, stdin
);
3835 reply(221, "You could at least say goodbye.");
3837 } else if (ret
== -2) {
3838 /* XXX: should we do this reply(-530, ..) ? */
3839 reply(-530, "Command too long.");
3842 /* Ensure it is PASS */
3843 if (strncasecmp(pbuf
, "PASS ", 5) != 0) {
3845 "auth_conv: unexpected reply '%.4s'", pbuf
);
3846 /* XXX: should we do this reply(-530, ..) ? */
3847 reply(-530, "Unexpected reply '%.4s'.", pbuf
);
3850 /* Strip CRLF from "PASS" reply */
3853 (pbuf
[n
] == '\r' || pbuf
[n
] == '\n'))
3855 /* Copy password into reply */
3856 myreply
[i
].resp
= ftpd_strdup(pbuf
+5);
3857 /* PAM frees resp. */
3862 default: /* unknown message style */
3873 return PAM_CONV_ERR
;
3877 * Attempt to authenticate the user using PAM. Returns 0 if the user is
3878 * authenticated, or 1 if not authenticated. If some sort of PAM system
3879 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3880 * function returns -1. This can be used as an indication that we should
3881 * fall back to a different authentication mechanism.
3882 * pw maybe be updated to a new user if PAM_USER changes from curname.
3887 const char *tmpl_user
;
3891 ftpd_cred_t auth_cred
= { curname
, 0 };
3892 struct pam_conv conv
= { &auth_conv
, &auth_cred
};
3894 e
= pam_start("ftpd", curname
, &conv
, &pamh
);
3895 if (e
!= PAM_SUCCESS
) {
3897 * In OpenPAM, it's OK to pass NULL to pam_strerror()
3898 * if context creation has failed in the first place.
3900 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(NULL
, e
));
3904 e
= pam_set_item(pamh
, PAM_RHOST
, remotehost
);
3905 if (e
!= PAM_SUCCESS
) {
3906 syslog(LOG_ERR
, "pam_set_item(PAM_RHOST): %s",
3907 pam_strerror(pamh
, e
));
3908 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3909 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3915 e
= pam_set_item(pamh
, PAM_SOCKADDR
, &his_addr
);
3916 if (e
!= PAM_SUCCESS
) {
3917 syslog(LOG_ERR
, "pam_set_item(PAM_SOCKADDR): %s",
3918 pam_strerror(pamh
, e
));
3919 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3920 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3926 e
= pam_authenticate(pamh
, 0);
3928 syslog(LOG_DEBUG
, "pam_authenticate: user '%s' returned %d",
3933 * With PAM we support the concept of a "template"
3934 * user. The user enters a login name which is
3935 * authenticated by PAM, usually via a remote service
3936 * such as RADIUS or TACACS+. If authentication
3937 * succeeds, a different but related "template" name
3938 * is used for setting the credentials, shell, and
3939 * home directory. The name the user enters need only
3940 * exist on the remote authentication server, but the
3941 * template name must be present in the local password
3944 * This is supported by two various mechanisms in the
3945 * individual modules. However, from the application's
3946 * point of view, the template user is always passed
3947 * back as a changed value of the PAM_USER item.
3949 if ((e
= pam_get_item(pamh
, PAM_USER
, &item
)) ==
3951 tmpl_user
= (const char *) item
;
3953 || strcmp(pw
->pw_name
, tmpl_user
) != 0) {
3954 pw
= sgetpwnam(tmpl_user
);
3957 "auth_pam: PAM changed "
3958 "user from '%s' to '%s'",
3959 curname
, pw
->pw_name
);
3960 (void)strlcpy(curname
, pw
->pw_name
,
3964 syslog(LOG_ERR
, "Couldn't get PAM_USER: %s",
3965 pam_strerror(pamh
, e
));
3970 case PAM_USER_UNKNOWN
:
3976 syslog(LOG_ERR
, "pam_authenticate: %s", pam_strerror(pamh
, e
));
3982 e
= pam_acct_mgmt(pamh
, 0);
3983 if (e
!= PAM_SUCCESS
) {
3984 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
3985 pam_strerror(pamh
, e
));
3991 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3992 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3999 #endif /* USE_PAM */