1 /* $NetBSD: ftpd.c,v 1.202 2015/08/10 07:32:49 shm 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.202 2015/08/10 07:32:49 shm 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>
172 #include "pathnames.h"
175 static sig_atomic_t transflag
;
176 static sig_atomic_t urgflag
;
181 int stru
; /* avoid C keyword */
183 int dataport
; /* use specific data port */
184 int dopidfile
; /* maintain pid file */
185 int doutmp
; /* update utmp file */
186 int dowtmp
; /* update wtmp file */
187 int doxferlog
; /* syslog/write wu-ftpd style xferlog entries */
188 int xferlogfd
; /* fd to write wu-ftpd xferlog entries to */
189 int getnameopts
; /* flags for use with getname() */
190 int dropprivs
; /* if privileges should or have been dropped */
191 int mapped
; /* IPv4 connection on AF_INET6 socket */
194 static char ttyline
[20];
197 static int auth_pam(void);
198 pam_handle_t
*pamh
= NULL
;
202 static struct utmp utmp
; /* for utmp */
205 static struct utmpx utmpx
; /* for utmpx */
208 static const char *anondir
= NULL
;
209 static const char *confdir
= _DEFAULT_CONFDIR
;
211 static char *curname
; /* current USER name */
212 static size_t curname_len
; /* length of curname (include NUL) */
214 #if defined(KERBEROS) || defined(KERBEROS5)
217 char *krbtkfile_env
= NULL
;
219 int login_krb5_forwardable_tgt
= 0;
225 * Timeout intervals for retrying connections
226 * to hosts that don't accept PORT cmds. This
227 * is a kludge, but given the problems with TCP...
229 #define SWAITMAX 90 /* wait at most 90 seconds */
230 #define SWAITINT 5 /* interval between retries */
232 int swaitmax
= SWAITMAX
;
233 int swaitint
= SWAITINT
;
237 SS_ABORTED
, /* transfer aborted */
238 SS_NO_TRANSFER
, /* no transfer made yet */
239 SS_FILE_ERROR
, /* file read error */
240 SS_DATA_ERROR
/* data send error */
243 static int bind_pasv_addr(void);
244 static int checkuser(const char *, const char *, int, int, char **);
245 static int checkaccess(const char *);
246 static int checkpassword(const struct passwd
*, const char *);
247 static void do_pass(int, int, const char *);
248 static void end_login(void);
249 static FILE *getdatasock(const char *);
250 static char *gunique(const char *);
251 static void login_utmp(const char *, const char *, const char *,
253 static void logremotehost(struct sockinet
*);
254 __dead
static void lostconn(int);
255 __dead
static void toolong(int);
256 __dead
static void sigquit(int);
257 static void sigurg(int);
258 static int handleoobcmd(void);
259 static int receive_data(FILE *, FILE *);
260 static int send_data(FILE *, FILE *, const struct stat
*, int);
261 static struct passwd
*sgetpwnam(const char *);
262 static int write_data(int, char *, size_t, off_t
*, struct timeval
*,
264 static enum send_status
265 send_data_with_read(int, int, const struct stat
*, int);
266 #if !defined(__minix)
267 static enum send_status
268 send_data_with_mmap(int, int, const struct stat
*, int);
269 #endif /* !defined(__minix) */
270 static void logrusage(const struct rusage
*, const struct rusage
*);
271 static void logout_utmp(void);
273 int main(int, char *[]);
275 #if defined(KERBEROS)
276 int klogin(struct passwd
*, char *, char *, char *);
279 #if defined(KERBEROS5)
280 int k5login(struct passwd
*, char *, char *, char *);
281 void k5destroy(void);
285 main(int argc
, char *argv
[])
287 int ch
, on
= 1, tos
, keepalive
;
290 krb5_error_code kerror
;
293 const char *xferlogname
= NULL
;
296 sa_family_t af
= AF_UNSPEC
;
305 dopidfile
= 1; /* default: DO use a pid file to count users */
306 doutmp
= 0; /* default: Do NOT log to utmp */
307 dowtmp
= 1; /* default: DO log to wtmp */
308 doxferlog
= 0; /* default: Do NOT syslog xferlog */
309 xferlogfd
= -1; /* default: Do NOT write xferlog file */
310 getnameopts
= 0; /* default: xlate addrs to name */
319 version
= FTPD_VERSION
;
322 * LOG_NDELAY sets up the logging connection immediately,
323 * necessary for anonymous ftp's that chroot and can't do it later.
325 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
327 while ((ch
= getopt(argc
, argv
,
328 "46a:c:C:Dde:h:HlL:nP:qQrst:T:uUvV:wWX")) != -1) {
347 if ((p
= strchr(optarg
, '@')) != NULL
) {
349 strlcpy(remotehost
, p
, MAXHOSTNAMELEN
+ 1);
350 if (inet_pton(AF_INET
, p
,
351 &his_addr
.su_addr
) == 1) {
352 his_addr
.su_family
= AF_INET
;
354 sizeof(his_addr
.si_su
.su_sin
);
356 } else if (inet_pton(AF_INET6
, p
,
357 &his_addr
.su_6addr
) == 1) {
358 his_addr
.su_family
= AF_INET6
;
360 sizeof(his_addr
.si_su
.su_sin6
);
363 his_addr
.su_family
= AF_UNSPEC
;
365 pw
= sgetpwnam(optarg
);
366 exit(checkaccess(optarg
) ? 0 : 1);
374 case 'v': /* deprecated */
383 strlcpy(hostname
, optarg
, sizeof(hostname
));
387 if (gethostname(hostname
, sizeof(hostname
)) == -1)
389 hostname
[sizeof(hostname
) - 1] = '\0';
393 logging
++; /* > 1 == extra logging */
397 xferlogname
= optarg
;
401 getnameopts
= NI_NUMERICHOST
;
407 l
= strtol(optarg
, &p
, 10);
408 if (errno
|| *optarg
== '\0' || *p
!= '\0' ||
409 l
< IPPORT_RESERVED
||
410 l
> IPPORT_ANONMAX
) {
411 syslog(LOG_WARNING
, "Invalid dataport %s",
437 "-%c has been deprecated in favour of ftpd.conf",
450 if (EMPTYSTR(optarg
) || strcmp(optarg
, "-") == 0)
453 version
= ftpd_strdup(optarg
);
469 if (optopt
== 'a' || optopt
== 'C')
471 syslog(LOG_WARNING
, "unknown flag -%c ignored", optopt
);
475 if (EMPTYSTR(confdir
))
476 confdir
= _DEFAULT_CONFDIR
;
489 #if !defined(__minix)
490 l
= sysconf(_SC_LOGIN_NAME_MAX
);
491 if (l
== -1 && errno
!= 0) {
492 syslog(LOG_ERR
, "sysconf _SC_LOGIN_NAME_MAX: %m");
495 syslog(LOG_WARNING
, "using conservative LOGIN_NAME_MAX value");
496 curname_len
= _POSIX_LOGIN_NAME_MAX
;
498 curname_len
= (size_t)l
;
500 curname_len
= _POSIX_LOGIN_NAME_MAX
;
501 #endif /* !defined(__minix) */
502 curname
= malloc(curname_len
);
503 if (curname
== NULL
) {
504 syslog(LOG_ERR
, "malloc: %m");
510 int error
, fd
, i
, n
, *socks
;
512 struct addrinfo hints
, *res
, *res0
;
514 if (daemon(1, 0) == -1) {
515 syslog(LOG_ERR
, "failed to daemonize: %m");
518 (void)memset(&sa
, 0, sizeof(sa
));
519 sa
.sa_handler
= SIG_IGN
;
520 sa
.sa_flags
= SA_NOCLDWAIT
;
521 sigemptyset(&sa
.sa_mask
);
522 (void)sigaction(SIGCHLD
, &sa
, NULL
);
524 (void)memset(&hints
, 0, sizeof(hints
));
525 hints
.ai_flags
= AI_PASSIVE
;
526 hints
.ai_family
= af
;
527 hints
.ai_socktype
= SOCK_STREAM
;
528 error
= getaddrinfo(NULL
, "ftp", &hints
, &res0
);
530 syslog(LOG_ERR
, "getaddrinfo: %s", gai_strerror(error
));
534 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
)
537 syslog(LOG_ERR
, "no addresses available");
540 socks
= malloc(n
* sizeof(int));
541 fds
= malloc(n
* sizeof(struct pollfd
));
542 if (socks
== NULL
|| fds
== NULL
) {
543 syslog(LOG_ERR
, "malloc: %m");
547 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
) {
548 socks
[n
] = socket(res
->ai_family
, res
->ai_socktype
,
552 (void)setsockopt(socks
[n
], SOL_SOCKET
, SO_REUSEADDR
,
554 if (bind(socks
[n
], res
->ai_addr
, res
->ai_addrlen
)
556 (void)close(socks
[n
]);
559 if (listen(socks
[n
], 12) == -1) {
560 (void)close(socks
[n
]);
564 fds
[n
].fd
= socks
[n
];
565 fds
[n
].events
= POLLIN
;
569 syslog(LOG_ERR
, "%m");
574 if (pidfile(NULL
) == -1)
575 syslog(LOG_ERR
, "failed to write a pid file: %m");
580 * MINIX 3 does not yet support SA_NOCLDWAIT. Cleaning
581 * up zombies this way is not perfect, but at least it
582 * prevents accumulation of zombies over time.
584 while (waitpid(-1, NULL
, WNOHANG
) > 0)
587 if (poll(fds
, n
, INFTIM
) == -1) {
590 syslog(LOG_ERR
, "poll: %m");
593 for (i
= 0; i
< n
; i
++) {
594 if (fds
[i
].revents
& POLLIN
) {
595 fd
= accept(fds
[i
].fd
, NULL
, NULL
);
597 syslog(LOG_ERR
, "accept: %m");
602 syslog(LOG_ERR
, "fork: %m");
613 (void)dup2(fd
, STDIN_FILENO
);
614 (void)dup2(fd
, STDOUT_FILENO
);
615 (void)dup2(fd
, STDERR_FILENO
);
616 for (i
= 0; i
< n
; i
++)
617 (void)close(socks
[i
]);
620 memset((char *)&his_addr
, 0, sizeof(his_addr
));
621 addrlen
= sizeof(his_addr
.si_su
);
622 if (getpeername(0, (struct sockaddr
*)&his_addr
.si_su
, &addrlen
) < 0) {
623 syslog((errno
== ENOTCONN
) ? LOG_NOTICE
: LOG_ERR
,
624 "getpeername (%s): %m",argv
[0]);
627 his_addr
.su_len
= addrlen
;
628 memset((char *)&ctrl_addr
, 0, sizeof(ctrl_addr
));
629 addrlen
= sizeof(ctrl_addr
.si_su
);
630 if (getsockname(0, (struct sockaddr
*)&ctrl_addr
, &addrlen
) < 0) {
631 syslog(LOG_ERR
, "getsockname (%s): %m",argv
[0]);
634 ctrl_addr
.su_len
= addrlen
;
636 if (his_addr
.su_family
== AF_INET6
637 && IN6_IS_ADDR_V4MAPPED(&his_addr
.su_6addr
)) {
640 * IPv4 control connection arrived to AF_INET6 socket.
641 * I hate to do this, but this is the easiest solution.
643 * The assumption is untrue on SIIT environment.
645 struct sockinet tmp_addr
;
646 const int off
= sizeof(struct in6_addr
) - sizeof(struct in_addr
);
649 memset(&his_addr
, 0, sizeof(his_addr
));
650 his_addr
.su_family
= AF_INET
;
651 his_addr
.su_len
= sizeof(his_addr
.si_su
.su_sin
);
652 memcpy(&his_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
653 sizeof(his_addr
.su_addr
));
654 his_addr
.su_port
= tmp_addr
.su_port
;
656 tmp_addr
= ctrl_addr
;
657 memset(&ctrl_addr
, 0, sizeof(ctrl_addr
));
658 ctrl_addr
.su_family
= AF_INET
;
659 ctrl_addr
.su_len
= sizeof(ctrl_addr
.si_su
.su_sin
);
660 memcpy(&ctrl_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
661 sizeof(ctrl_addr
.su_addr
));
662 ctrl_addr
.su_port
= tmp_addr
.su_port
;
664 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
665 if ((cp
= strchr(line
, '\n')) != NULL
)
667 reply(-530, "%s", line
);
669 (void) fflush(stdout
);
672 "Connection from IPv4 mapped address is not supported.");
681 if (!mapped
&& his_addr
.su_family
== AF_INET
) {
682 tos
= IPTOS_LOWDELAY
;
683 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
685 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
688 /* if the hostname hasn't been given, attempt to determine it */
689 if (hostname
[0] == '\0') {
690 if (getnameinfo((struct sockaddr
*)&ctrl_addr
.si_su
,
691 ctrl_addr
.su_len
, hostname
, sizeof(hostname
), NULL
, 0,
693 (void)gethostname(hostname
, sizeof(hostname
));
694 hostname
[sizeof(hostname
) - 1] = '\0';
697 /* set this here so klogin can use it... */
698 (void)snprintf(ttyline
, sizeof(ttyline
), "ftp%d", getpid());
700 (void) freopen(_PATH_DEVNULL
, "w", stderr
);
702 memset(&sa
, 0, sizeof(sa
));
703 sa
.sa_handler
= SIG_DFL
;
704 sa
.sa_flags
= SA_RESTART
;
705 sigemptyset(&sa
.sa_mask
);
706 (void) sigaction(SIGCHLD
, &sa
, NULL
);
708 sa
.sa_handler
= sigquit
;
709 sa
.sa_flags
= SA_RESTART
;
710 sigfillset(&sa
.sa_mask
); /* block all sigs in these handlers */
711 (void) sigaction(SIGHUP
, &sa
, NULL
);
712 (void) sigaction(SIGINT
, &sa
, NULL
);
713 (void) sigaction(SIGQUIT
, &sa
, NULL
);
714 (void) sigaction(SIGTERM
, &sa
, NULL
);
715 sa
.sa_handler
= lostconn
;
716 (void) sigaction(SIGPIPE
, &sa
, NULL
);
717 sa
.sa_handler
= toolong
;
718 (void) sigaction(SIGALRM
, &sa
, NULL
);
719 sa
.sa_handler
= sigurg
;
720 (void) sigaction(SIGURG
, &sa
, NULL
);
722 /* Try to handle urgent data inline */
724 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *)&on
, sizeof(on
)) < 0)
725 syslog(LOG_WARNING
, "setsockopt: %m");
727 /* Set keepalives on the socket to detect dropped connections. */
730 if (setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&keepalive
,
732 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
736 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
737 syslog(LOG_WARNING
, "fcntl F_SETOWN: %m");
739 logremotehost(&his_addr
);
741 * Set up default state
752 kerror
= krb5_init_context(&kcontext
);
754 syslog(LOG_ERR
, "%s when initializing Kerberos context",
755 error_message(kerror
));
758 #endif /* KERBEROS5 */
761 curclass
.timeout
= 300; /* 5 minutes, as per login(1) */
762 curclass
.type
= CLASS_REAL
;
764 /* If logins are disabled, print out the message. */
765 if (display_file(_PATH_NOLOGIN
, 530)) {
766 reply(530, "System not available.");
769 (void)display_file(conffilename(_NAME_FTPWELCOME
), 220);
770 /* reply(220,) must follow */
771 if (EMPTYSTR(version
))
772 reply(220, "%s FTP server ready.", hostname
);
774 reply(220, "%s FTP server (%s) ready.", hostname
, version
);
776 if (xferlogname
!= NULL
) {
777 xferlogfd
= open(xferlogname
, O_WRONLY
| O_APPEND
| O_CREAT
,
780 syslog(LOG_WARNING
, "open xferlog `%s': %m",
791 lostconn(int signo __unused
)
795 syslog(LOG_DEBUG
, "lost connection");
800 toolong(int signo __unused
)
805 "Timeout (" LLF
" seconds): closing control connection.",
806 (LLT
)curclass
.timeout
);
808 syslog(LOG_INFO
, "User %s timed out after " LLF
" seconds",
809 (pw
? pw
->pw_name
: "unknown"), (LLT
)curclass
.timeout
);
818 syslog(LOG_DEBUG
, "got signal %d", signo
);
823 sigurg(int signo __unused
)
831 * Save the result of a getpwnam. Used for USER command, since
832 * the data returned must not be clobbered by any other command
835 static struct passwd
*
836 sgetpwnam(const char *name
)
838 static struct passwd save
;
841 if ((p
= getpwnam(name
)) == NULL
)
844 free((char *)save
.pw_name
);
845 memset(save
.pw_passwd
, 0, strlen(save
.pw_passwd
));
846 free((char *)save
.pw_passwd
);
847 free((char *)save
.pw_gecos
);
848 free((char *)save
.pw_dir
);
849 free((char *)save
.pw_shell
);
852 save
.pw_name
= ftpd_strdup(p
->pw_name
);
853 save
.pw_passwd
= ftpd_strdup(p
->pw_passwd
);
854 save
.pw_gecos
= ftpd_strdup(p
->pw_gecos
);
855 save
.pw_dir
= ftpd_strdup(p
->pw_dir
);
856 save
.pw_shell
= ftpd_strdup(p
->pw_shell
);
860 static int login_attempts
; /* number of failed login attempts */
861 static int askpasswd
; /* had USER command, ask for PASSwd */
862 static int permitted
; /* USER permitted */
866 * Sets global passwd pointer pw if named account exists and is acceptable;
867 * sets askpasswd if a PASS command is expected. If logged in previously,
868 * need to reset state. If name is "ftp" or "anonymous", the name is not in
869 * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
870 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
871 * requesting login privileges. Disallow anyone who does not have a standard
872 * shell as returned by getusershell(). Disallow anyone mentioned in the file
873 * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
876 user(const char *name
)
880 login_cap_t
*lc
= NULL
;
888 switch (curclass
.type
) {
890 reply(530, "Can't change user from guest login.");
893 reply(530, "Can't change user from chroot user.");
897 reply(530, "Can't change user.");
907 #if defined(KERBEROS)
910 #if defined(KERBEROS5)
914 curclass
.type
= CLASS_REAL
;
918 if (strcmp(name
, "ftp") == 0 || strcmp(name
, "anonymous") == 0) {
919 /* need `pw' setup for checkaccess() and checkuser () */
920 if ((pw
= sgetpwnam("ftp")) == NULL
)
921 reply(530, "User %s unknown.", name
);
922 else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
923 reply(530, "User %s access denied.", name
);
925 curclass
.type
= CLASS_GUEST
;
928 "Guest login ok, type your name as password.");
933 "ANONYMOUS FTP LOGIN REFUSED FROM %s",
940 pw
= sgetpwnam(name
);
942 strlcpy(curname
, name
, curname_len
);
944 /* check user in /etc/ftpusers, and setup class */
945 permitted
= checkuser(_NAME_FTPUSERS
, curname
, 1, 0, &class);
947 /* check user in /etc/ftpchroot */
949 lc
= login_getpwclass(pw
);
951 if (checkuser(_NAME_FTPCHROOT
, curname
, 0, 0, NULL
)
952 #ifdef LOGIN_CAP /* Allow login.conf configuration as well */
953 || login_getcapbool(lc
, "ftp-chroot", 0)
956 if (curclass
.type
== CLASS_GUEST
) {
958 "Can't change guest user to chroot class; remove entry in %s",
962 curclass
.type
= CLASS_CHROOT
;
965 /* determine default class */
967 switch (curclass
.type
) {
969 class = ftpd_strdup("guest");
972 class = ftpd_strdup("chroot");
975 class = ftpd_strdup("real");
978 syslog(LOG_ERR
, "unknown curclass.type %d; aborting",
983 /* parse ftpd.conf, setting up various parameters */
985 /* if not guest user, check for valid shell */
989 const char *cp
, *shell
;
991 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
992 shell
= _PATH_BSHELL
;
993 while ((cp
= getusershell()) != NULL
)
994 if (strcmp(cp
, shell
) == 0)
997 if (cp
== NULL
&& curclass
.type
!= CLASS_GUEST
)
1001 /* deny quickly (after USER not PASS) if requested */
1002 if (CURCLASS_FLAGS_ISSET(denyquick
) && !permitted
) {
1003 reply(530, "User %s may not use FTP.", curname
);
1005 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
1006 remoteloghost
, curname
);
1011 /* if haven't asked yet (i.e, not anon), ask now */
1015 e
= auth_pam(); /* this does reply(331, ...) */
1018 #else /* !USE_PAM */
1020 if (skey_haskey(curname
) == 0) {
1023 myskey
= skey_keyinfo(curname
);
1024 reply(331, "Password [ %s ] required for %s.",
1025 myskey
? myskey
: "error getting challenge",
1029 reply(331, "Password required for %s.", curname
);
1030 #endif /* !USE_PAM */
1038 * Delay before reading passwd after first failed
1039 * attempt to slow down passwd-guessing programs.
1042 sleep((unsigned) login_attempts
);
1049 * Determine whether something is to happen (allow access, chroot)
1050 * for a user. Each line is a shell-style glob followed by
1053 * For backward compatibility, `allow' and `deny' are synonymns
1054 * for `yes' and `no', respectively.
1056 * Each glob is matched against the username in turn, and the first
1057 * match found is used. If no match is found, the result is the
1058 * argument `def'. If a match is found but without and explicit
1059 * `yes'/`no', the result is the opposite of def.
1061 * If the file doesn't exist at all, the result is the argument
1064 * Any line starting with `#' is considered a comment and ignored.
1066 * Returns 0 if the user is denied, or 1 if they are allowed.
1068 * NOTE: needs struct passwd *pw setup before use.
1071 checkuser(const char *fname
, const char *name
, int def
, int nofile
,
1076 char *word
, *perm
, *class, *buf
, *p
;
1080 if (retclass
!= NULL
)
1082 if ((fd
= fopen(conffilename(fname
), "r")) == NULL
)
1087 (buf
= fparseln(fd
, &len
, &line
, NULL
, FPARSELN_UNESCCOMM
|
1088 FPARSELN_UNESCCONT
| FPARSELN_UNESCESC
)) != NULL
;
1089 free(buf
), buf
= NULL
) {
1090 word
= perm
= class = NULL
;
1094 if (p
[len
- 1] == '\n')
1104 if (!EMPTYSTR(class)) {
1105 if (strcasecmp(class, "all") == 0 ||
1106 strcasecmp(class, "none") == 0) {
1108 "%s line %d: illegal user-defined class `%s' - skipping entry",
1109 fname
, (int)line
, class);
1114 /* have a host specifier */
1115 if ((p
= strchr(word
, '@')) != NULL
) {
1116 unsigned char net
[16], mask
[16], *addr
;
1117 int addrlen
, bits
, bytes
, a
;
1120 /* check against network or CIDR */
1121 memset(net
, 0x00, sizeof(net
));
1122 if ((bits
= inet_net_pton(his_addr
.su_family
, p
, net
,
1123 sizeof(net
))) != -1) {
1125 if (his_addr
.su_family
== AF_INET
) {
1128 addr
= (unsigned char *)&his_addr
.su_addr
;
1132 addr
= (unsigned char *)&his_addr
.su_6addr
;
1138 memset(mask
, 0xFF, bytes
);
1139 if (bytes
< addrlen
)
1140 mask
[bytes
] = 0xFF << (8 - bits
);
1141 if (bytes
+ 1 < addrlen
)
1142 memset(mask
+ bytes
+ 1, 0x00,
1143 addrlen
- bytes
- 1);
1144 for (a
= 0; a
< addrlen
; a
++)
1145 if ((addr
[a
] & mask
[a
]) != net
[a
])
1150 /* check against hostname glob */
1151 } else if (fnmatch(p
, remotehost
, FNM_CASEFOLD
) != 0)
1155 /* have a group specifier */
1156 if ((p
= strchr(word
, ':')) != NULL
) {
1158 int gsize
, i
, found
;
1161 continue; /* no match for unknown user */
1166 ng
= realloc(groups
, gsize
* sizeof(gid_t
));
1169 "Local resource failure: realloc");
1171 } while (getgrouplist(pw
->pw_name
, pw
->pw_gid
,
1172 groups
, &gsize
) == -1);
1174 for (i
= 0; i
< gsize
; i
++) {
1177 if ((g
= getgrgid(groups
[i
])) == NULL
)
1179 if (fnmatch(p
, g
->gr_name
, 0) == 0) {
1189 /* check against username glob */
1190 if (fnmatch(word
, name
, 0) != 0)
1194 ((strcasecmp(perm
, "allow") == 0) ||
1195 (strcasecmp(perm
, "yes") == 0)))
1197 else if (perm
!= NULL
&&
1198 ((strcasecmp(perm
, "deny") == 0) ||
1199 (strcasecmp(perm
, "no") == 0)))
1203 if (!EMPTYSTR(class) && retclass
!= NULL
)
1204 *retclass
= ftpd_strdup(class);
1213 * Check if user is allowed by /etc/ftpusers
1214 * returns 1 for yes, 0 for no
1216 * NOTE: needs struct passwd *pw setup (for checkuser())
1219 checkaccess(const char *name
)
1222 return (checkuser(_NAME_FTPUSERS
, name
, 1, 0, NULL
));
1226 login_utmp(const char *line
, const char *name
, const char *host
,
1227 struct sockinet
*haddr
)
1229 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1231 (void)gettimeofday(&tv
, NULL
);
1233 #ifdef SUPPORT_UTMPX
1235 (void)memset(&utmpx
, 0, sizeof(utmpx
));
1237 utmpx
.ut_pid
= getpid();
1238 utmpx
.ut_id
[0] = 'f';
1239 utmpx
.ut_id
[1] = 't';
1240 utmpx
.ut_id
[2] = 'p';
1241 utmpx
.ut_id
[3] = '*';
1242 utmpx
.ut_type
= USER_PROCESS
;
1243 (void)strncpy(utmpx
.ut_name
, name
, sizeof(utmpx
.ut_name
));
1244 (void)strncpy(utmpx
.ut_line
, line
, sizeof(utmpx
.ut_line
));
1245 (void)strncpy(utmpx
.ut_host
, host
, sizeof(utmpx
.ut_host
));
1246 (void)memcpy(&utmpx
.ut_ss
, &haddr
->si_su
, haddr
->su_len
);
1247 ftpd_loginx(&utmpx
);
1250 ftpd_logwtmpx(line
, name
, host
, haddr
, 0, USER_PROCESS
);
1254 (void)memset(&utmp
, 0, sizeof(utmp
));
1255 (void)time(&utmp
.ut_time
);
1256 (void)strncpy(utmp
.ut_name
, name
, sizeof(utmp
.ut_name
));
1257 (void)strncpy(utmp
.ut_line
, line
, sizeof(utmp
.ut_line
));
1258 (void)strncpy(utmp
.ut_host
, host
, sizeof(utmp
.ut_host
));
1262 ftpd_logwtmp(line
, name
, host
);
1269 #ifdef SUPPORT_UTMPX
1270 int okwtmpx
= dowtmp
;
1273 int okwtmp
= dowtmp
;
1276 #ifdef SUPPORT_UTMPX
1278 okwtmpx
&= ftpd_logoutx(ttyline
, 0, DEAD_PROCESS
);
1280 ftpd_logwtmpx(ttyline
, "", "", NULL
, 0, DEAD_PROCESS
);
1284 okwtmp
&= ftpd_logout(ttyline
);
1286 ftpd_logwtmp(ttyline
, "", "");
1292 * Terminate login as previous user (if any), resetting state;
1293 * used when USER command is given or login fails.
1302 show_chdir_messages(-1); /* flush chdir cache */
1303 if (pw
!= NULL
&& pw
->pw_passwd
!= NULL
)
1304 memset(pw
->pw_passwd
, 0, strlen(pw
->pw_passwd
));
1311 curclass
.type
= CLASS_REAL
;
1312 (void) seteuid((uid_t
)0);
1314 setusercontext(NULL
, getpwuid(0), 0,
1315 LOGIN_SETPRIORITY
|LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1319 if ((e
= pam_setcred(pamh
, PAM_DELETE_CRED
)) != PAM_SUCCESS
)
1320 syslog(LOG_ERR
, "pam_setcred: %s",
1321 pam_strerror(pamh
, e
));
1322 if ((e
= pam_close_session(pamh
,0)) != PAM_SUCCESS
)
1323 syslog(LOG_ERR
, "pam_close_session: %s",
1324 pam_strerror(pamh
, e
));
1325 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
)
1326 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
1333 pass(const char *passwd
)
1335 do_pass(0, 0, passwd
);
1339 * Perform the passwd confirmation and login.
1341 * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval.
1342 * This is the traditional PASS implementation.
1344 * If pass_checked is non-zero, use pass_rval and ignore passwd.
1345 * This is used by auth_pam() which has already parsed PASS.
1346 * This only applies to curclass.type != CLASS_GUEST.
1349 do_pass(int pass_checked
, int pass_rval
, const char *passwd
)
1352 char root
[MAXPATHLEN
];
1354 login_cap_t
*lc
= NULL
;
1362 if (logged_in
|| askpasswd
== 0) {
1363 reply(503, "Login with USER first.");
1367 if (curclass
.type
!= CLASS_GUEST
) {
1368 /* "ftp" is the only account allowed with no password */
1370 rval
= 1; /* failure below */
1373 if (pass_checked
) { /* password validated in user() */
1378 syslog(LOG_ERR
, "do_pass: USE_PAM shouldn't get here");
1382 #if defined(KERBEROS)
1383 if (klogin(pw
, "", hostname
, (char *)passwd
) == 0) {
1388 #if defined(KERBEROS5)
1389 if (k5login(pw
, "", hostname
, (char *)passwd
) == 0) {
1395 if (skey_haskey(pw
->pw_name
) == 0) {
1399 p
= ftpd_strdup(passwd
);
1400 r
= skey_passcheck(pw
->pw_name
, p
);
1409 rval
= checkpassword(pw
, passwd
);
1416 * If rval > 0, the user failed the authentication check
1417 * above. If rval == 0, either Kerberos or local
1418 * authentication succeeded.
1421 reply(530, "%s", rval
== 2 ? "Password expired." :
1422 "Login incorrect.");
1423 pfilter_notify(1, rval
== 2 ? "exppass" : "badpass");
1426 "FTP LOGIN FAILED FROM %s", remoteloghost
);
1427 syslog(LOG_AUTHPRIV
| LOG_NOTICE
,
1428 "FTP LOGIN FAILED FROM %s, %s",
1429 remoteloghost
, curname
);
1432 if (login_attempts
++ >= 5) {
1434 "repeated login failures from %s",
1442 /* password ok; check if anything else prevents login */
1444 reply(530, "User %s may not use FTP.", pw
->pw_name
);
1446 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
1447 remoteloghost
, pw
->pw_name
);
1451 login_attempts
= 0; /* this time successful */
1452 if (setegid((gid_t
)pw
->pw_gid
) < 0) {
1453 reply(550, "Can't set gid.");
1457 if ((lc
= login_getpwclass(pw
)) != NULL
) {
1459 char remote_ip
[NI_MAXHOST
];
1461 if (getnameinfo((struct sockaddr
*)&his_addr
, his_addr
.su_len
,
1462 remote_ip
, sizeof(remote_ip
) - 1, NULL
, 0,
1465 remote_ip
[sizeof(remote_ip
) - 1] = 0;
1466 if (!auth_hostok(lc
, remotehost
, remote_ip
)) {
1467 pfilter_notify(1, "bannedhost");
1468 syslog(LOG_INFO
|LOG_AUTH
,
1469 "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1471 reply(530, "Permission denied.");
1475 if (!auth_timeok(lc
, time(NULL
))) {
1476 reply(530, "Login not available right now.");
1483 setusercontext(lc
, pw
, 0,
1484 LOGIN_SETLOGIN
|LOGIN_SETGROUP
|LOGIN_SETPRIORITY
|
1485 LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1487 (void) initgroups(pw
->pw_name
, pw
->pw_gid
);
1488 /* cache groups for cmds.c::matchgroup() */
1492 if ((e
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
1493 syslog(LOG_ERR
, "pam_open_session: %s",
1494 pam_strerror(pamh
, e
));
1495 } else if ((e
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
))
1497 syslog(LOG_ERR
, "pam_setcred: %s",
1498 pam_strerror(pamh
, e
));
1502 gidcount
= getgroups(0, NULL
);
1505 gidlist
= malloc(gidcount
* sizeof *gidlist
);
1506 gidcount
= getgroups(gidcount
, gidlist
);
1508 /* open utmp/wtmp before chroot */
1509 login_utmp(ttyline
, pw
->pw_name
, remotehost
, &his_addr
);
1516 if (curclass
.limit
!= -1 && connections
> curclass
.limit
) {
1517 if (! EMPTYSTR(curclass
.limitfile
))
1518 (void)display_file(conffilename(curclass
.limitfile
),
1521 "User %s access denied, connection limit of " LLF
1523 pw
->pw_name
, (LLT
)curclass
.limit
);
1525 "Maximum connection limit of " LLF
1526 " for class %s reached, login refused for %s",
1527 (LLT
)curclass
.limit
, curclass
.classname
, pw
->pw_name
);
1532 switch (curclass
.type
) {
1535 * We MUST do a chdir() after the chroot. Otherwise
1536 * the old current directory will be accessible as "."
1537 * outside the new root!
1540 curclass
.chroot
? curclass
.chroot
:
1543 format_path(homedir
,
1544 curclass
.homedir
? curclass
.homedir
:
1546 if (EMPTYSTR(homedir
))
1548 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1550 "GUEST user %s: can't chroot to %s: %m",
1554 if (chdir(homedir
) < 0) {
1556 "GUEST user %s: can't chdir to %s: %m",
1557 pw
->pw_name
, homedir
);
1559 reply(550, "Can't set guest privileges.");
1565 curclass
.chroot
? curclass
.chroot
:
1567 format_path(homedir
,
1568 curclass
.homedir
? curclass
.homedir
:
1570 if (EMPTYSTR(homedir
))
1572 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1574 "CHROOT user %s: can't chroot to %s: %m",
1578 if (chdir(homedir
) < 0) {
1580 "CHROOT user %s: can't chdir to %s: %m",
1581 pw
->pw_name
, homedir
);
1583 reply(550, "Can't change root.");
1588 /* only chroot REAL if explicitly requested */
1589 if (! EMPTYSTR(curclass
.chroot
)) {
1590 format_path(root
, curclass
.chroot
);
1591 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1593 "REAL user %s: can't chroot to %s: %m",
1598 format_path(homedir
,
1599 curclass
.homedir
? curclass
.homedir
:
1601 if (EMPTYSTR(homedir
) || chdir(homedir
) < 0) {
1602 if (chdir("/") < 0) {
1604 "REAL user %s: can't chdir to %s: %m",
1606 !EMPTYSTR(homedir
) ? homedir
: "/");
1608 "User %s: can't change directory to %s.",
1610 !EMPTYSTR(homedir
) ? homedir
: "/");
1614 "No directory! Logging in with home=/");
1622 #if !defined(__minix)
1623 setlogin(pw
->pw_name
);
1624 #endif /* !defined(__minix) */
1627 (curclass
.type
!= CLASS_REAL
&&
1628 ntohs(ctrl_addr
.su_port
) > IPPORT_RESERVED
+ 1)) {
1630 if (setgid((gid_t
)pw
->pw_gid
) < 0) {
1631 reply(550, "Can't set gid.");
1634 if (setuid((uid_t
)pw
->pw_uid
) < 0) {
1635 reply(550, "Can't set uid.");
1639 if (seteuid((uid_t
)pw
->pw_uid
) < 0) {
1640 reply(550, "Can't set uid.");
1644 setenv("HOME", homedir
, 1);
1646 if (curclass
.type
== CLASS_GUEST
&& passwd
[0] == '-')
1650 * Display a login message, if it exists.
1651 * N.B. reply(230,) must follow the message.
1653 if (! EMPTYSTR(curclass
.motd
))
1654 (void)display_file(conffilename(curclass
.motd
), 230);
1655 show_chdir_messages(230);
1656 if (curclass
.type
== CLASS_GUEST
) {
1659 reply(230, "Guest login ok, access restrictions apply.");
1660 #if defined(HAVE_SETPROCTITLE)
1661 snprintf(proctitle
, sizeof(proctitle
),
1662 "%s: anonymous/%s", remotehost
, passwd
);
1663 setproctitle("%s", proctitle
);
1664 #endif /* defined(HAVE_SETPROCTITLE) */
1667 "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1668 remoteloghost
, passwd
,
1669 curclass
.classname
, CURCLASSTYPE
);
1670 /* store guest password reply into pw_passwd */
1671 REASSIGN(pw
->pw_passwd
, ftpd_strdup(passwd
));
1672 for (p
= pw
->pw_passwd
; *p
; p
++)
1673 if (!isgraph((unsigned char)*p
))
1676 reply(230, "User %s logged in.", pw
->pw_name
);
1677 #if defined(HAVE_SETPROCTITLE)
1678 snprintf(proctitle
, sizeof(proctitle
),
1679 "%s: %s", remotehost
, pw
->pw_name
);
1680 setproctitle("%s", proctitle
);
1681 #endif /* defined(HAVE_SETPROCTITLE) */
1684 "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1685 remoteloghost
, pw
->pw_name
,
1686 curclass
.classname
, CURCLASSTYPE
);
1688 (void) umask(curclass
.umask
);
1698 /* Forget all about it... */
1703 retrieve(const char *argv
[], const char *name
)
1707 int (*closefunc
)(FILE *) = NULL
;
1708 int dolog
, sendrv
, closerv
, stderrfd
, isconversion
, isdata
, isls
;
1709 struct timeval start
, finish
, td
, *tdp
;
1710 struct rusage rusage_before
, rusage_after
;
1711 const char *dispname
;
1714 sendrv
= closerv
= stderrfd
= -1;
1715 isconversion
= isdata
= isls
= dolog
= 0;
1720 if (argv
== NULL
) { /* if not running a command ... */
1723 fin
= fopen(name
, "r");
1725 if (fin
== NULL
) /* doesn't exist?; try a conversion */
1726 argv
= do_conversion(name
);
1729 syslog(LOG_DEBUG
, "get command: '%s' on '%s'",
1734 char temp
[MAXPATHLEN
];
1736 if (strcmp(argv
[0], INTERNAL_LS
) == 0) {
1740 (void)snprintf(temp
, sizeof(temp
), "%s", TMPFILE
);
1741 stderrfd
= mkstemp(temp
);
1746 fin
= ftpd_popen(argv
, "r", stderrfd
);
1747 closefunc
= ftpd_pclose
;
1749 st
.st_blksize
= BUFSIZ
;
1753 perror_reply(550, dispname
);
1755 logxfer("get", -1, name
, NULL
, NULL
,
1758 goto cleanupretrieve
;
1762 && (fstat(fileno(fin
), &st
) < 0 || !S_ISREG(st
.st_mode
))) {
1763 error
= "Not a plain file";
1764 reply(550, "%s: %s.", dispname
, error
);
1767 if (restart_point
) {
1768 if (type
== TYPE_A
) {
1772 for (i
= 0; i
< restart_point
; i
++) {
1773 if ((c
=getc(fin
)) == EOF
) {
1774 error
= strerror(errno
);
1775 perror_reply(550, dispname
);
1781 } else if (lseek(fileno(fin
), restart_point
, SEEK_SET
) < 0) {
1782 error
= strerror(errno
);
1783 perror_reply(550, dispname
);
1787 dout
= dataconn(dispname
, st
.st_size
, "w");
1791 (void)getrusage(RUSAGE_SELF
, &rusage_before
);
1792 (void)gettimeofday(&start
, NULL
);
1793 sendrv
= send_data(fin
, dout
, &st
, isdata
);
1794 (void)gettimeofday(&finish
, NULL
);
1795 (void)getrusage(RUSAGE_SELF
, &rusage_after
);
1796 closedataconn(dout
); /* close now to affect timing stats */
1797 timersub(&finish
, &start
, &td
);
1801 logxfer("get", byte_count
, name
, NULL
, tdp
, error
);
1803 logrusage(&rusage_before
, &rusage_after
);
1805 closerv
= (*closefunc
)(fin
);
1810 if (!isls
&& argv
!= NULL
&& closerv
!= 0) {
1812 "Command returned an exit status of %d",
1816 "retrieve command: '%s' returned %d",
1819 if (!isls
&& argv
!= NULL
&& stderrfd
!= -1 &&
1820 (fstat(stderrfd
, &sb
) == 0) && sb
.st_size
> 0 &&
1821 ((errf
= fdopen(stderrfd
, "r")) != NULL
)) {
1822 char *cp
, line
[LINE_MAX
];
1824 reply(-226, "Command error messages:");
1826 while (fgets(line
, sizeof(line
), errf
) != NULL
) {
1827 if ((cp
= strchr(line
, '\n')) != NULL
)
1829 reply(0, " %s", line
);
1831 (void) fflush(stdout
);
1832 (void) fclose(errf
);
1833 /* a reply(226,) must follow */
1835 reply(226, "Transfer complete.");
1839 (void)close(stderrfd
);
1845 store(const char *name
, const char *fmode
, int unique
)
1849 int (*closefunc
)(FILE *);
1850 struct timeval start
, finish
, td
, *tdp
;
1851 const char *desc
, *error
;
1854 desc
= (*fmode
== 'w') ? "put" : "append";
1856 if (unique
&& stat(name
, &st
) == 0 &&
1857 (name
= gunique(name
)) == NULL
) {
1858 logxfer(desc
, -1, name
, NULL
, NULL
,
1859 "cannot create unique file");
1865 fout
= fopen(name
, fmode
);
1869 perror_reply(553, name
);
1870 logxfer(desc
, -1, name
, NULL
, NULL
, strerror(errno
));
1874 if (restart_point
) {
1875 if (type
== TYPE_A
) {
1879 for (i
= 0; i
< restart_point
; i
++) {
1880 if ((c
=getc(fout
)) == EOF
) {
1881 error
= strerror(errno
);
1882 perror_reply(550, name
);
1889 * We must do this seek to "current" position
1890 * because we are changing from reading to
1893 if (fseek(fout
, 0L, SEEK_CUR
) < 0) {
1894 error
= strerror(errno
);
1895 perror_reply(550, name
);
1898 } else if (lseek(fileno(fout
), restart_point
, SEEK_SET
) < 0) {
1899 error
= strerror(errno
);
1900 perror_reply(550, name
);
1904 din
= dataconn(name
, (off_t
)-1, "r");
1907 (void)gettimeofday(&start
, NULL
);
1908 if (receive_data(din
, fout
) == 0) {
1910 reply(226, "Transfer complete (unique file name:%s).",
1913 reply(226, "Transfer complete.");
1915 (void)gettimeofday(&finish
, NULL
);
1916 closedataconn(din
); /* close now to affect timing stats */
1917 timersub(&finish
, &start
, &td
);
1920 logxfer(desc
, byte_count
, name
, NULL
, tdp
, error
);
1927 getdatasock(const char *fmode
)
1929 int on
, s
, t
, tries
;
1934 return (fdopen(data
, fmode
));
1936 (void) seteuid((uid_t
)0);
1937 s
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
1940 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
1941 (char *) &on
, sizeof(on
)) < 0)
1943 if (setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
1944 (char *) &on
, sizeof(on
)) < 0)
1946 /* anchor socket to avoid multi-homing problems */
1947 data_source
= ctrl_addr
;
1949 * By default source port for PORT connctions is
1950 * ctrlport-1 (see RFC959 section 5.2).
1951 * However, if privs have been dropped and that
1952 * would be < IPPORT_RESERVED, use a random port
1958 port
= ntohs(ctrl_addr
.su_port
) - 1;
1959 if (dropprivs
&& port
< IPPORT_RESERVED
)
1960 port
= 0; /* use random port */
1961 data_source
.su_port
= htons(port
);
1963 for (tries
= 1; ; tries
++) {
1964 if (bind(s
, (struct sockaddr
*)&data_source
.si_su
,
1965 data_source
.su_len
) >= 0)
1967 if (errno
!= EADDRINUSE
|| tries
> 10)
1972 (void) seteuid((uid_t
)pw
->pw_uid
);
1974 if (!mapped
&& ctrl_addr
.su_family
== AF_INET
) {
1975 on
= IPTOS_THROUGHPUT
;
1976 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *)&on
,
1978 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
1981 return (fdopen(s
, fmode
));
1983 /* Return the real value of errno (close may change it) */
1986 (void) seteuid((uid_t
)pw
->pw_uid
);
1993 dataconn(const char *name
, off_t size
, const char *fmode
)
1997 int retry
, tos
, keepalive
, conerrno
;
2001 if (size
!= (off_t
) -1)
2002 (void)snprintf(sizebuf
, sizeof(sizebuf
), " (" LLF
" byte%s)",
2003 (LLT
)size
, PLURAL(size
));
2007 struct sockinet from
;
2009 socklen_t fromlen
= sizeof(from
.su_len
);
2011 (void) alarm(curclass
.timeout
);
2012 s
= accept(pdata
, (struct sockaddr
*)&from
.si_su
, &fromlen
);
2015 reply(425, "Can't open data connection.");
2016 (void) close(pdata
);
2020 (void) close(pdata
);
2022 switch (from
.su_family
) {
2026 tos
= IPTOS_THROUGHPUT
;
2027 (void) setsockopt(s
, IPPROTO_IP
, IP_TOS
,
2028 (char *)&tos
, sizeof(int));
2033 /* Set keepalives on the socket to detect dropped conns. */
2036 (void) setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
2037 (char *)&keepalive
, sizeof(int));
2039 reply(150, "Opening %s mode data connection for '%s'%s.",
2040 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2041 return (fdopen(pdata
, fmode
));
2044 reply(125, "Using existing data connection for '%s'%s.",
2047 return (fdopen(data
, fmode
));
2050 data_dest
= his_addr
;
2052 retry
= conerrno
= 0;
2054 file
= getdatasock(fmode
);
2056 char hbuf
[NI_MAXHOST
];
2057 char pbuf
[NI_MAXSERV
];
2059 if (getnameinfo((struct sockaddr
*)&data_source
.si_su
,
2060 data_source
.su_len
, hbuf
, sizeof(hbuf
), pbuf
,
2061 sizeof(pbuf
), NI_NUMERICHOST
| NI_NUMERICSERV
))
2062 strlcpy(hbuf
, "?", sizeof(hbuf
));
2063 reply(425, "Can't create data socket (%s,%s): %s.",
2064 hbuf
, pbuf
, strerror(errno
));
2067 data
= fileno(file
);
2069 if (connect(data
, (struct sockaddr
*)&data_dest
.si_su
,
2070 data_dest
.su_len
) == 0)
2073 (void) fclose(file
);
2076 if (conerrno
== EADDRINUSE
) {
2077 sleep((unsigned) swaitint
);
2082 } while (retry
<= swaitmax
);
2083 if (conerrno
!= 0) {
2084 perror_reply(425, "Can't build data connection");
2087 reply(150, "Opening %s mode data connection for '%s'%s.",
2088 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2093 closedataconn(FILE *fd
)
2106 write_data(int fd
, char *buf
, size_t size
, off_t
*bufrem
,
2107 struct timeval
*then
, int isdata
)
2109 struct timeval now
, td
;
2114 if (curclass
.writesize
) {
2115 if (curclass
.writesize
< c
)
2116 c
= curclass
.writesize
;
2118 if (curclass
.rateget
) {
2122 (void) alarm(curclass
.timeout
);
2123 c
= write(fd
, buf
, c
);
2130 total_data_out
+= c
;
2133 total_bytes_out
+= c
;
2135 if (curclass
.rateget
) {
2138 (void)gettimeofday(&now
, NULL
);
2139 timersub(&now
, then
, &td
);
2140 if (td
.tv_sec
== 0) {
2141 usleep(1000000 - td
.tv_usec
);
2142 (void)gettimeofday(then
, NULL
);
2145 *bufrem
= curclass
.rateget
;
2152 static enum send_status
2153 send_data_with_read(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2155 struct timeval then
;
2161 if (curclass
.readsize
> 0)
2162 readsize
= curclass
.readsize
;
2164 readsize
= st
->st_blksize
;
2165 if ((buf
= malloc(readsize
)) == NULL
) {
2166 perror_reply(451, "Local resource failure: malloc");
2167 return (SS_NO_TRANSFER
);
2170 if (curclass
.rateget
) {
2171 bufrem
= curclass
.rateget
;
2172 (void)gettimeofday(&then
, NULL
);
2176 (void) alarm(curclass
.timeout
);
2177 c
= read(filefd
, buf
, readsize
);
2181 error
= SS_FILE_ERROR
;
2182 else if (write_data(netfd
, buf
, c
, &bufrem
, &then
, isdata
))
2183 error
= SS_DATA_ERROR
;
2184 else if (urgflag
&& handleoobcmd())
2194 static enum send_status
2195 send_data_with_mmap(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2197 struct timeval then
;
2198 off_t bufrem
, filesize
, off
, origoff
;
2199 ssize_t mapsize
, winsize
;
2200 int error
, sendbufsize
, sendlowat
;
2204 if (curclass
.sendbufsize
) {
2205 sendbufsize
= curclass
.sendbufsize
;
2206 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDBUF
,
2207 &sendbufsize
, sizeof(int)) == -1)
2208 syslog(LOG_WARNING
, "setsockopt(SO_SNDBUF, %d): %m",
2212 if (curclass
.sendlowat
) {
2213 sendlowat
= curclass
.sendlowat
;
2214 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDLOWAT
,
2215 &sendlowat
, sizeof(int)) == -1)
2216 syslog(LOG_WARNING
, "setsockopt(SO_SNDLOWAT, %d): %m",
2220 winsize
= curclass
.mmapsize
;
2221 filesize
= st
->st_size
;
2223 syslog(LOG_INFO
, "mmapsize = " LLF
", writesize = " LLF
,
2224 (LLT
)winsize
, (LLT
)curclass
.writesize
);
2228 off
= lseek(filefd
, (off_t
)0, SEEK_CUR
);
2233 if (curclass
.rateget
) {
2234 bufrem
= curclass
.rateget
;
2235 (void)gettimeofday(&then
, NULL
);
2239 mapsize
= MIN(filesize
- off
, winsize
);
2242 win
= mmap(NULL
, mapsize
, PROT_READ
,
2243 MAP_FILE
|MAP_SHARED
, filefd
, off
);
2244 if (win
== MAP_FAILED
) {
2247 return (SS_FILE_ERROR
);
2249 #if !defined(__minix)
2250 (void) madvise(win
, mapsize
, MADV_SEQUENTIAL
);
2251 #endif /* !defined(__minix) */
2252 error
= write_data(netfd
, win
, mapsize
, &bufrem
, &then
,
2254 #if !defined(__minix)
2255 (void) madvise(win
, mapsize
, MADV_DONTNEED
);
2256 #endif /* !defined(__minix) */
2257 munmap(win
, mapsize
);
2258 if (urgflag
&& handleoobcmd())
2259 return (SS_ABORTED
);
2261 return (SS_DATA_ERROR
);
2264 return (SS_SUCCESS
);
2267 return (send_data_with_read(filefd
, netfd
, st
, isdata
));
2271 * Transfer the contents of "instr" to "outstr" peer using the appropriate
2272 * encapsulation of the data subject to Mode, Structure, and Type.
2274 * NB: Form isn't handled.
2277 send_data(FILE *instr
, FILE *outstr
, const struct stat
*st
, int isdata
)
2279 int c
, filefd
, netfd
, rval
;
2288 /* XXXLUKEM: rate limit ascii send (get) */
2289 (void) alarm(curclass
.timeout
);
2290 while ((c
= getc(instr
)) != EOF
) {
2291 if (urgflag
&& handleoobcmd())
2292 goto cleanup_send_data
;
2297 (void) putc('\r', outstr
);
2305 (void) putc(c
, outstr
);
2312 if ((byte_count
% 4096) == 0)
2313 (void) alarm(curclass
.timeout
);
2322 goto cleanup_send_data
;
2326 filefd
= fileno(instr
);
2327 netfd
= fileno(outstr
);
2328 switch (send_data_with_mmap(filefd
, netfd
, st
, isdata
)) {
2334 case SS_NO_TRANSFER
:
2335 goto cleanup_send_data
;
2344 goto cleanup_send_data
;
2347 reply(550, "Unimplemented TYPE %d in send_data", type
);
2348 goto cleanup_send_data
;
2353 perror_reply(426, "Data connection");
2354 goto cleanup_send_data
;
2358 perror_reply(551, "Error on input file");
2359 goto cleanup_send_data
;
2375 * Transfer data from peer to "outstr" using the appropriate encapulation of
2376 * the data subject to Mode, Structure, and Type.
2378 * N.B.: Form isn't handled.
2381 receive_data(FILE *instr
, FILE *outstr
)
2383 int c
, netfd
, filefd
, rval
;
2384 int volatile bare_lfs
;
2388 struct sigaction sa
, sa_saved
;
2391 memset(&sa
, 0, sizeof(sa
));
2392 sigfillset(&sa
.sa_mask
);
2393 sa
.sa_flags
= SA_RESTART
;
2394 sa
.sa_handler
= lostconn
;
2395 (void) sigaction(SIGALRM
, &sa
, &sa_saved
);
2404 #define FILESIZECHECK(x) \
2406 if (curclass.maxfilesize != -1 && \
2407 (x) > curclass.maxfilesize) { \
2417 netfd
= fileno(instr
);
2418 filefd
= fileno(outstr
);
2419 (void) alarm(curclass
.timeout
);
2420 if (curclass
.readsize
)
2421 readsize
= curclass
.readsize
;
2422 else if (fstat(filefd
, &st
) != -1)
2423 readsize
= (ssize_t
)st
.st_blksize
;
2426 if ((buf
= malloc(readsize
)) == NULL
) {
2427 perror_reply(451, "Local resource failure: malloc");
2428 goto cleanup_recv_data
;
2430 if (curclass
.rateput
) {
2433 struct timeval then
, now
, td
;
2436 (void)gettimeofday(&then
, NULL
);
2438 for (bufrem
= curclass
.rateput
; bufrem
> 0; ) {
2439 if ((c
= read(netfd
, buf
,
2440 MIN(readsize
, bufrem
))) <= 0)
2442 if (urgflag
&& handleoobcmd())
2443 goto cleanup_recv_data
;
2444 FILESIZECHECK(byte_count
+ c
);
2445 if ((d
= write(filefd
, buf
, c
)) != c
)
2447 (void) alarm(curclass
.timeout
);
2452 total_bytes_in
+= c
;
2455 (void)gettimeofday(&now
, NULL
);
2456 timersub(&now
, &then
, &td
);
2458 usleep(1000000 - td
.tv_usec
);
2461 while ((c
= read(netfd
, buf
, readsize
)) > 0) {
2462 if (urgflag
&& handleoobcmd())
2463 goto cleanup_recv_data
;
2464 FILESIZECHECK(byte_count
+ c
);
2465 if (write(filefd
, buf
, c
) != c
)
2467 (void) alarm(curclass
.timeout
);
2471 total_bytes_in
+= c
;
2479 goto cleanup_recv_data
;
2482 reply(553, "TYPE E not implemented.");
2483 goto cleanup_recv_data
;
2486 (void) alarm(curclass
.timeout
);
2487 /* XXXLUKEM: rate limit ascii receive (put) */
2488 while ((c
= getc(instr
)) != EOF
) {
2489 if (urgflag
&& handleoobcmd())
2490 goto cleanup_recv_data
;
2496 if ((byte_count
% 4096) == 0)
2497 (void) alarm(curclass
.timeout
);
2503 if ((c
= getc(instr
)) != '\n') {
2509 if ((byte_count
% 4096) == 0)
2510 (void) alarm(curclass
.timeout
);
2512 FILESIZECHECK(byteswritten
);
2513 (void) putc ('\r', outstr
);
2514 if (c
== '\0' || c
== EOF
)
2519 FILESIZECHECK(byteswritten
);
2520 (void) putc(c
, outstr
);
2531 "WARNING! %d bare linefeeds received in ASCII mode",
2533 reply(0, "File may not have transferred correctly.");
2536 goto cleanup_recv_data
;
2539 reply(550, "Unimplemented TYPE %d in receive_data", type
);
2540 goto cleanup_recv_data
;
2542 #undef FILESIZECHECK
2546 perror_reply(426, "Data Connection");
2547 goto cleanup_recv_data
;
2551 perror_reply(452, "Error writing file");
2552 goto cleanup_recv_data
;
2556 (void) sigaction(SIGALRM
, &sa_saved
, NULL
);
2571 struct sockinet
*su
= NULL
;
2572 static char hbuf
[NI_MAXHOST
], sbuf
[NI_MAXSERV
];
2573 unsigned char *a
, *p
;
2575 off_t otbi
, otbo
, otb
;
2579 reply(-211, "%s FTP server status:", hostname
);
2580 reply(0, "Version: %s", EMPTYSTR(version
) ? "<suppressed>" : version
);
2582 if (!getnameinfo((struct sockaddr
*)&his_addr
.si_su
, his_addr
.su_len
,
2583 hbuf
, sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
)
2584 && strcmp(remotehost
, hbuf
) != 0)
2585 reply(0, "Connected to %s (%s)", remotehost
, hbuf
);
2587 reply(0, "Connected to %s", remotehost
);
2590 if (curclass
.type
== CLASS_GUEST
)
2591 reply(0, "Logged in anonymously");
2593 reply(0, "Logged in as %s%s", pw
->pw_name
,
2594 curclass
.type
== CLASS_CHROOT
? " (chroot)" : "");
2595 } else if (askpasswd
)
2596 reply(0, "Waiting for password");
2598 reply(0, "Waiting for user name");
2599 cprintf(stdout
, " TYPE: %s", typenames
[type
]);
2600 if (type
== TYPE_A
|| type
== TYPE_E
)
2601 cprintf(stdout
, ", FORM: %s", formnames
[form
]);
2602 if (type
== TYPE_L
) {
2604 cprintf(stdout
, " %d", NBBY
);
2606 /* XXX: `bytesize' needs to be defined in this case */
2607 cprintf(stdout
, " %d", bytesize
);
2610 cprintf(stdout
, "; STRUcture: %s; transfer MODE: %s\r\n",
2611 strunames
[stru
], modenames
[mode
]);
2614 reply(0, "Data connection open");
2616 } else if (pdata
!= -1) {
2617 reply(0, "in Passive mode");
2618 if (curclass
.advertise
.su_len
!= 0)
2619 su
= &curclass
.advertise
;
2624 } else if (usedefault
== 0) {
2625 su
= (struct sockinet
*)&data_dest
;
2628 reply(0, "EPSV only mode (EPSV ALL)");
2633 if (su
->su_family
== AF_INET
) {
2634 a
= (unsigned char *) &su
->su_addr
;
2635 p
= (unsigned char *) &su
->su_port
;
2636 #define UC(b) (((int) b) & 0xff)
2637 reply(0, "%s (%d,%d,%d,%d,%d,%d)",
2638 ispassive
? "PASV" : "PORT" ,
2639 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
2640 UC(p
[0]), UC(p
[1]));
2648 switch (su
->su_family
) {
2650 a
= (unsigned char *) &su
->su_addr
;
2651 p
= (unsigned char *) &su
->su_port
;
2652 alen
= sizeof(su
->su_addr
);
2657 a
= (unsigned char *) &su
->su_6addr
;
2658 p
= (unsigned char *) &su
->su_port
;
2659 alen
= sizeof(su
->su_6addr
);
2668 cprintf(stdout
, " %s (%d,%d",
2669 ispassive
? "LPSV" : "LPRT", af
, alen
);
2670 for (i
= 0; i
< alen
; i
++)
2671 cprintf(stdout
, ",%d", UC(a
[i
]));
2672 cprintf(stdout
, ",%d,%d,%d)\r\n",
2673 2, UC(p
[0]), UC(p
[1]));
2680 af
= af2epsvproto(su
->su_family
);
2683 struct sockinet tmp
;
2687 if (tmp
.su_family
== AF_INET6
)
2688 tmp
.su_scope_id
= 0;
2690 if (getnameinfo((struct sockaddr
*)&tmp
.si_su
,
2691 tmp
.su_len
, hbuf
, sizeof(hbuf
), sbuf
, sizeof(sbuf
),
2692 NI_NUMERICHOST
| NI_NUMERICSERV
) == 0)
2693 reply(0, "%s (|%d|%s|%s|)",
2694 ispassive
? "EPSV" : "EPRT",
2698 reply(0, "No data connection");
2702 "Data sent: " LLF
" byte%s in " LLF
" file%s",
2703 (LLT
)total_data_out
, PLURAL(total_data_out
),
2704 (LLT
)total_files_out
, PLURAL(total_files_out
));
2706 "Data received: " LLF
" byte%s in " LLF
" file%s",
2707 (LLT
)total_data_in
, PLURAL(total_data_in
),
2708 (LLT
)total_files_in
, PLURAL(total_files_in
));
2710 "Total data: " LLF
" byte%s in " LLF
" file%s",
2711 (LLT
)total_data
, PLURAL(total_data
),
2712 (LLT
)total_files
, PLURAL(total_files
));
2714 otbi
= total_bytes_in
;
2715 otbo
= total_bytes_out
;
2717 reply(0, "Traffic sent: " LLF
" byte%s in " LLF
" transfer%s",
2718 (LLT
)otbo
, PLURAL(otbo
),
2719 (LLT
)total_xfers_out
, PLURAL(total_xfers_out
));
2720 reply(0, "Traffic received: " LLF
" byte%s in " LLF
" transfer%s",
2721 (LLT
)otbi
, PLURAL(otbi
),
2722 (LLT
)total_xfers_in
, PLURAL(total_xfers_in
));
2723 reply(0, "Total traffic: " LLF
" byte%s in " LLF
" transfer%s",
2724 (LLT
)otb
, PLURAL(otb
),
2725 (LLT
)total_xfers
, PLURAL(total_xfers
));
2727 if (logged_in
&& !CURCLASS_FLAGS_ISSET(private)) {
2731 reply(0, "Class: %s, type: %s",
2732 curclass
.classname
, CURCLASSTYPE
);
2733 reply(0, "Check PORT/LPRT commands: %sabled",
2734 CURCLASS_FLAGS_ISSET(checkportcmd
) ? "en" : "dis");
2735 if (! EMPTYSTR(curclass
.display
))
2736 reply(0, "Display file: %s", curclass
.display
);
2737 if (! EMPTYSTR(curclass
.notify
))
2738 reply(0, "Notify fileglob: %s", curclass
.notify
);
2739 reply(0, "Idle timeout: " LLF
", maximum timeout: " LLF
,
2740 (LLT
)curclass
.timeout
, (LLT
)curclass
.maxtimeout
);
2741 reply(0, "Current connections: %d", connections
);
2742 if (curclass
.limit
== -1)
2743 reply(0, "Maximum connections: unlimited");
2745 reply(0, "Maximum connections: " LLF
,
2746 (LLT
)curclass
.limit
);
2747 if (curclass
.limitfile
)
2748 reply(0, "Connection limit exceeded message file: %s",
2749 conffilename(curclass
.limitfile
));
2750 if (! EMPTYSTR(curclass
.chroot
))
2751 reply(0, "Chroot format: %s", curclass
.chroot
);
2752 reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2753 CURCLASS_FLAGS_ISSET(denyquick
) ? "en" : "dis");
2754 if (! EMPTYSTR(curclass
.homedir
))
2755 reply(0, "Homedir format: %s", curclass
.homedir
);
2756 if (curclass
.maxfilesize
== -1)
2757 reply(0, "Maximum file size: unlimited");
2759 reply(0, "Maximum file size: " LLF
,
2760 (LLT
)curclass
.maxfilesize
);
2761 if (! EMPTYSTR(curclass
.motd
))
2762 reply(0, "MotD file: %s", conffilename(curclass
.motd
));
2764 "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2765 CURCLASS_FLAGS_ISSET(modify
) ? "en" : "dis");
2766 reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2767 CURCLASS_FLAGS_ISSET(upload
) ? "en" : "dis");
2768 reply(0, "Sanitize file names: %sabled",
2769 CURCLASS_FLAGS_ISSET(sanenames
) ? "en" : "dis");
2770 reply(0, "PASV/LPSV/EPSV connections: %sabled",
2771 CURCLASS_FLAGS_ISSET(passive
) ? "en" : "dis");
2772 if (curclass
.advertise
.su_len
!= 0) {
2773 char buf
[50]; /* big enough for IPv6 address */
2776 bp
= inet_ntop(curclass
.advertise
.su_family
,
2777 (void *)&curclass
.advertise
.su_addr
,
2780 reply(0, "PASV advertise address: %s", bp
);
2782 if (curclass
.portmin
&& curclass
.portmax
)
2783 reply(0, "PASV port range: " LLF
" - " LLF
,
2784 (LLT
)curclass
.portmin
, (LLT
)curclass
.portmax
);
2785 if (curclass
.rateget
)
2786 reply(0, "Rate get limit: " LLF
" bytes/sec",
2787 (LLT
)curclass
.rateget
);
2789 reply(0, "Rate get limit: disabled");
2790 if (curclass
.rateput
)
2791 reply(0, "Rate put limit: " LLF
" bytes/sec",
2792 (LLT
)curclass
.rateput
);
2794 reply(0, "Rate put limit: disabled");
2795 if (curclass
.mmapsize
)
2796 reply(0, "Mmap size: " LLF
, (LLT
)curclass
.mmapsize
);
2798 reply(0, "Mmap size: disabled");
2799 if (curclass
.readsize
)
2800 reply(0, "Read size: " LLF
, (LLT
)curclass
.readsize
);
2802 reply(0, "Read size: default");
2803 if (curclass
.writesize
)
2804 reply(0, "Write size: " LLF
, (LLT
)curclass
.writesize
);
2806 reply(0, "Write size: default");
2807 if (curclass
.recvbufsize
)
2808 reply(0, "Receive buffer size: " LLF
,
2809 (LLT
)curclass
.recvbufsize
);
2811 reply(0, "Receive buffer size: default");
2812 if (curclass
.sendbufsize
)
2813 reply(0, "Send buffer size: " LLF
,
2814 (LLT
)curclass
.sendbufsize
);
2816 reply(0, "Send buffer size: default");
2817 if (curclass
.sendlowat
)
2818 reply(0, "Send low water mark: " LLF
,
2819 (LLT
)curclass
.sendlowat
);
2821 reply(0, "Send low water mark: default");
2822 reply(0, "Umask: %.04o", curclass
.umask
);
2823 for (cp
= curclass
.conversions
; cp
!= NULL
; cp
=cp
->next
) {
2824 if (cp
->suffix
== NULL
|| cp
->types
== NULL
||
2825 cp
->command
== NULL
)
2827 reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2828 cp
->suffix
, cp
->types
, cp
->disable
, cp
->command
);
2832 reply(211, "End of status");
2836 fatal(const char *s
)
2839 reply(451, "Error in server: %s\n", s
);
2840 reply(221, "Closing connection due to server error.");
2847 * depending on the value of n, display fmt with a trailing CRLF and
2849 * n < -1 prefix the message with abs(n) + "-" (initial line)
2850 * n == 0 prefix the message with 4 spaces (middle lines)
2851 * n > 0 prefix the message with n + " " (final line)
2854 reply(int n
, const char *fmt
, ...)
2856 char msg
[MAXPATHLEN
* 2 + 100];
2861 b
= snprintf(msg
, sizeof(msg
), " ");
2863 b
= snprintf(msg
, sizeof(msg
), "%d-", -n
);
2865 b
= snprintf(msg
, sizeof(msg
), "%d ", n
);
2867 vsnprintf(msg
+ b
, sizeof(msg
) - b
, fmt
, ap
);
2869 cprintf(stdout
, "%s\r\n", msg
);
2870 (void)fflush(stdout
);
2872 syslog(LOG_DEBUG
, "<--- %s", msg
);
2876 logremotehost(struct sockinet
*who
)
2879 #if defined(HAVE_SOCKADDR_SNPRINTF)
2883 struct sockaddr
*sa
= (struct sockaddr
*)&who
->si_su
;
2884 if (getnameinfo(sa
, who
->su_len
, remotehost
, sizeof(remotehost
), NULL
,
2886 strlcpy(remotehost
, "?", sizeof(remotehost
));
2887 #if defined(HAVE_SOCKADDR_SNPRINTF)
2888 sockaddr_snprintf(abuf
, sizeof(abuf
), "%a", sa
);
2889 snprintf(remoteloghost
, sizeof(remoteloghost
), "%s(%s)", remotehost
,
2892 strlcpy(remoteloghost
, remotehost
, sizeof(remoteloghost
));
2895 #if defined(HAVE_SETPROCTITLE)
2896 snprintf(proctitle
, sizeof(proctitle
), "%s: connected", remotehost
);
2897 setproctitle("%s", proctitle
);
2898 #endif /* defined(HAVE_SETPROCTITLE) */
2900 syslog(LOG_INFO
, "connection from %s to %s",
2901 remoteloghost
, hostname
);
2905 * Record logout in wtmp file and exit with supplied status.
2906 * NOTE: because this is called from signal handlers it cannot
2907 * use stdio (or call other functions that use stdio).
2910 dologout(int status
)
2913 * Prevent reception of SIGURG from resulting in a resumption
2914 * back to the main program loop.
2920 if (!notickets
&& krbtkfile_env
)
2921 unlink(krbtkfile_env
);
2924 /* beware of flushing buffers after a SIGPIPE */
2925 if (xferlogfd
!= -1)
2938 reply(426, "Transfer aborted. Data connection closed.");
2939 reply(226, "Abort successful");
2940 transflag
= 0; /* flag that the transfer has aborted */
2951 if (file_size
!= (off_t
) -1)
2953 "Status: " LLF
" of " LLF
" byte%s transferred",
2954 (LLT
)byte_count
, (LLT
)file_size
,
2955 PLURAL(byte_count
));
2957 reply(213, "Status: " LLF
" byte%s transferred",
2958 (LLT
)byte_count
, PLURAL(byte_count
));
2962 * Call when urgflag != 0 to handle Out Of Band commands.
2963 * Returns non zero if the OOB command aborted the transfer
2964 * by setting transflag to 0. (c.f., "ABOR").
2975 /* only process if transfer occurring */
2979 ret
= get_line(cp
, sizeof(tmpline
)-1, stdin
);
2981 reply(221, "You could at least say goodbye.");
2983 } else if (ret
== -2) {
2984 /* Ignore truncated command */
2985 /* XXX: abort xfer with "500 command too long", & return 1 ? */
2989 * Manually parse OOB commands, because we can't
2990 * recursively call the yacc parser...
2992 if (strcasecmp(cp
, "ABOR\r\n") == 0) {
2994 } else if (strcasecmp(cp
, "STAT\r\n") == 0) {
2997 /* XXX: error with "500 unknown command" ? */
2999 return (transflag
== 0);
3003 bind_pasv_addr(void)
3005 static int passiveport
;
3008 len
= pasv_addr
.su_len
;
3009 if (curclass
.portmin
== 0 && curclass
.portmax
== 0) {
3010 pasv_addr
.su_port
= 0;
3011 return (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
));
3014 if (passiveport
== 0) {
3016 passiveport
= rand() % (curclass
.portmax
- curclass
.portmin
)
3023 if (port
> curclass
.portmax
)
3024 port
= curclass
.portmin
;
3025 else if (port
== passiveport
) {
3029 pasv_addr
.su_port
= htons(port
);
3030 if (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
) == 0)
3032 if (errno
!= EADDRINUSE
)
3040 * Note: a response of 425 is not mentioned as a possible response to
3041 * the PASV command in RFC959. However, it has been blessed as
3042 * a legitimate response by Jon Postel in a telephone conversation
3043 * with Rick Adams on 25 Jan 89.
3054 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
3055 if (pdata
< 0 || !logged_in
) {
3056 perror_reply(425, "Can't open passive connection");
3059 pasv_addr
= ctrl_addr
;
3061 if (bind_pasv_addr() < 0)
3063 len
= pasv_addr
.su_len
;
3064 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3066 pasv_addr
.su_len
= len
;
3067 if (curclass
.recvbufsize
) {
3068 recvbufsize
= curclass
.recvbufsize
;
3069 if (setsockopt(pdata
, SOL_SOCKET
, SO_RCVBUF
, &recvbufsize
,
3071 syslog(LOG_WARNING
, "setsockopt(SO_RCVBUF, %d): %m",
3074 if (listen(pdata
, 1) < 0)
3076 if (curclass
.advertise
.su_len
!= 0)
3077 a
= (char *) &curclass
.advertise
.su_addr
;
3079 a
= (char *) &pasv_addr
.su_addr
;
3080 p
= (char *) &pasv_addr
.su_port
;
3082 #define UC(b) (((int) b) & 0xff)
3084 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
3085 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
3089 (void) close(pdata
);
3091 perror_reply(425, "Can't open passive connection");
3096 * convert protocol identifier to/from AF
3099 lpsvproto2af(int proto
)
3115 af2lpsvproto(int af
)
3131 epsvproto2af(int proto
)
3147 af2epsvproto(int af
)
3163 * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3164 * 229 Entering Extended Passive Mode (|||port|)
3167 long_passive(const char *cmd
, int pf
)
3173 syslog(LOG_NOTICE
, "long passive but not logged in");
3174 reply(503, "Login with USER first.");
3178 if (pf
!= PF_UNSPEC
&& ctrl_addr
.su_family
!= pf
) {
3180 * XXX: only EPRT/EPSV ready clients will understand this
3182 if (strcmp(cmd
, "EPSV") != 0)
3183 reply(501, "Network protocol mismatch"); /*XXX*/
3185 epsv_protounsupp("Network protocol mismatch");
3192 pdata
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
3194 perror_reply(425, "Can't open passive connection");
3197 pasv_addr
= ctrl_addr
;
3198 if (bind_pasv_addr() < 0)
3200 len
= pasv_addr
.su_len
;
3201 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3203 pasv_addr
.su_len
= len
;
3204 if (listen(pdata
, 1) < 0)
3206 p
= (char *) &pasv_addr
.su_port
;
3208 #define UC(b) (((int) b) & 0xff)
3210 if (strcmp(cmd
, "LPSV") == 0) {
3211 struct sockinet
*advert
;
3213 if (curclass
.advertise
.su_len
!= 0)
3214 advert
= &curclass
.advertise
;
3216 advert
= &pasv_addr
;
3217 switch (advert
->su_family
) {
3219 a
= (char *) &advert
->su_addr
;
3221 "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3222 4, 4, UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3223 2, UC(p
[0]), UC(p
[1]));
3227 a
= (char *) &advert
->su_6addr
;
3229 "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)",
3231 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3232 UC(a
[4]), UC(a
[5]), UC(a
[6]), UC(a
[7]),
3233 UC(a
[8]), UC(a
[9]), UC(a
[10]), UC(a
[11]),
3234 UC(a
[12]), UC(a
[13]), UC(a
[14]), UC(a
[15]),
3235 2, UC(p
[0]), UC(p
[1]));
3240 } else if (strcmp(cmd
, "EPSV") == 0) {
3241 switch (pasv_addr
.su_family
) {
3246 reply(229, "Entering Extended Passive Mode (|||%d|)",
3247 ntohs(pasv_addr
.su_port
));
3251 /* more proper error code? */
3255 (void) close(pdata
);
3257 perror_reply(425, "Can't open passive connection");
3262 extended_port(const char *arg
)
3268 struct addrinfo hints
;
3269 struct addrinfo
*res
= NULL
;
3271 unsigned long proto
;
3273 tmp
= ftpd_strdup(arg
);
3277 memset(result
, 0, sizeof(result
));
3278 for (i
= 0; i
< 3; i
++) {
3279 q
= strchr(p
, delim
);
3280 if (!q
|| *q
!= delim
)
3287 /* some more sanity checks */
3290 (void)strtoul(result
[2], &p
, 10);
3291 if (errno
|| !*result
[2] || *p
)
3295 proto
= strtoul(result
[0], &p
, 10);
3296 if (errno
|| !*result
[0] || *p
)
3299 memset(&hints
, 0, sizeof(hints
));
3300 hints
.ai_family
= epsvproto2af((int)proto
);
3301 if (hints
.ai_family
< 0)
3303 hints
.ai_socktype
= SOCK_STREAM
;
3304 hints
.ai_flags
= AI_NUMERICHOST
;
3305 if (getaddrinfo(result
[1], result
[2], &hints
, &res
))
3309 if (sizeof(data_dest
) < res
->ai_addrlen
)
3311 memcpy(&data_dest
.si_su
, res
->ai_addr
, res
->ai_addrlen
);
3312 data_dest
.su_len
= res
->ai_addrlen
;
3314 if (his_addr
.su_family
== AF_INET6
&&
3315 data_dest
.su_family
== AF_INET6
) {
3316 /* XXX: more sanity checks! */
3317 data_dest
.su_scope_id
= his_addr
.su_scope_id
;
3328 reply(500, "Invalid argument, rejected.");
3337 epsv_protounsupp("Protocol not supported");
3345 * 522 Protocol not supported (proto,...)
3346 * as we assume address family for control and data connections are the same,
3347 * we do not return the list of address families we support - instead, we
3348 * return the address family of the control connection.
3351 epsv_protounsupp(const char *message
)
3355 proto
= af2epsvproto(ctrl_addr
.su_family
);
3357 reply(501, "%s", message
); /* XXX */
3359 reply(522, "%s, use (%d)", message
, proto
);
3363 * Generate unique name for file with basename "local".
3364 * The file named "local" is already known to exist.
3365 * Generates failure reply on error.
3367 * XXX: this function should under go changes similar to
3368 * the mktemp(3)/mkstemp(3) changes.
3371 gunique(const char *local
)
3373 static char new[MAXPATHLEN
];
3378 cp
= strrchr(local
, '/');
3381 if (stat(cp
? local
: ".", &st
) < 0) {
3382 perror_reply(553, cp
? local
: ".");
3387 for (count
= 1; count
< 100; count
++) {
3388 (void)snprintf(new, sizeof(new) - 1, "%s.%d", local
, count
);
3389 if (stat(new, &st
) < 0)
3392 reply(452, "Unique file name cannot be created.");
3397 * Format and send reply containing system error number.
3400 perror_reply(int code
, const char *string
)
3405 reply(code
, "%s: %s.", string
, strerror(errno
));
3409 static char *onefile
[] = {
3415 send_file_list(const char *whichf
)
3420 FILE *volatile dout
;
3421 char **volatile dirlist
;
3424 int volatile simple
;
3425 int volatile freeglob
;
3436 if (strpbrk(whichf
, "~{[*?") != NULL
) {
3437 int flags
= GLOB_BRACE
|GLOB_NOCHECK
|GLOB_TILDE
|GLOB_LIMIT
;
3439 memset(&gl
, 0, sizeof(gl
));
3441 if (glob(whichf
, flags
, 0, &gl
)) {
3442 reply(450, "Not found");
3443 goto cleanup_send_file_list
;
3444 } else if (gl
.gl_pathc
== 0) {
3446 perror_reply(450, whichf
);
3447 goto cleanup_send_file_list
;
3449 dirlist
= gl
.gl_pathv
;
3451 notglob
= ftpd_strdup(whichf
);
3452 onefile
[0] = notglob
;
3456 /* XXX: } for vi sm */
3458 while ((dirname
= *dirlist
++) != NULL
) {
3459 int trailingslash
= 0;
3461 if (stat(dirname
, &st
) < 0) {
3463 * If user typed "ls -l", etc, and the client
3464 * used NLST, do what the user meant.
3466 /* XXX: nuke this support? */
3467 if (dirname
[0] == '-' && *dirlist
== NULL
&&
3469 const char *argv
[] = { INTERNAL_LS
, "", NULL
};
3472 retrieve(argv
, dirname
);
3473 goto cleanup_send_file_list
;
3475 perror_reply(450, whichf
);
3476 goto cleanup_send_file_list
;
3479 if (S_ISREG(st
.st_mode
)) {
3482 * should we follow RFC959 and not work
3483 * for non directories?
3486 dout
= dataconn("file list", (off_t
)-1, "w");
3488 goto cleanup_send_file_list
;
3491 cprintf(dout
, "%s%s\n", dirname
,
3492 type
== TYPE_A
? "\r" : "");
3494 } else if (!S_ISDIR(st
.st_mode
))
3497 if (dirname
[strlen(dirname
) - 1] == '/')
3500 if ((dirp
= opendir(dirname
)) == NULL
)
3503 while ((dir
= readdir(dirp
)) != NULL
) {
3504 char nbuf
[MAXPATHLEN
];
3506 if (urgflag
&& handleoobcmd()) {
3507 (void) closedir(dirp
);
3508 goto cleanup_send_file_list
;
3511 if (ISDOTDIR(dir
->d_name
) || ISDOTDOTDIR(dir
->d_name
))
3514 (void)snprintf(nbuf
, sizeof(nbuf
), "%s%s%s", dirname
,
3515 trailingslash
? "" : "/", dir
->d_name
);
3518 * We have to do a stat to ensure it's
3519 * not a directory or special file.
3523 * should we follow RFC959 and filter out
3524 * non files ? lukem - NO!, or not until
3525 * our ftp client uses MLS{T,D} for completion.
3527 if (simple
|| (stat(nbuf
, &st
) == 0 &&
3528 S_ISREG(st
.st_mode
))) {
3530 dout
= dataconn("file list", (off_t
)-1,
3533 (void) closedir(dirp
);
3534 goto cleanup_send_file_list
;
3539 if (nbuf
[0] == '.' && nbuf
[1] == '/')
3541 cprintf(dout
, "%s%s\n", p
,
3542 type
== TYPE_A
? "\r" : "");
3545 (void) closedir(dirp
);
3549 reply(450, "No files found.");
3550 else if (ferror(dout
) != 0)
3551 perror_reply(451, "Data connection");
3553 reply(226, "Transfer complete.");
3555 cleanup_send_file_list
:
3556 closedataconn(dout
);
3568 conffilename(const char *s
)
3570 static char filename
[MAXPATHLEN
];
3573 strlcpy(filename
, s
, sizeof(filename
));
3575 (void)snprintf(filename
, sizeof(filename
), "%s/%s", confdir
,s
);
3581 * if logging > 1, then based on the arguments, syslog a message:
3582 * if bytes != -1 "<command> <file1> = <bytes> bytes"
3583 * else if file2 != NULL "<command> <file1> <file2>"
3584 * else "<command> <file1>"
3585 * if elapsed != NULL, append "in xxx.yyy seconds"
3586 * if error != NULL, append ": " + error
3588 * if doxferlog != 0, bytes != -1, and command is "get", "put",
3589 * or "append", syslog and/or write a wu-ftpd style xferlog entry
3592 logxfer(const char *command
, off_t bytes
, const char *file1
, const char *file2
,
3593 const struct timeval
*elapsed
, const char *error
)
3595 char buf
[MAXPATHLEN
* 2 + 100];
3596 char realfile1
[MAXPATHLEN
], realfile2
[MAXPATHLEN
];
3597 const char *r1
, *r2
;
3602 if (logging
<=1 && !doxferlog
)
3606 if ((r1
= realpath(file1
, realfile1
)) == NULL
)
3609 if ((r2
= realpath(file2
, realfile2
)) == NULL
)
3616 len
= snprintf(buf
, sizeof(buf
), "%s %s", command
, r1
);
3617 if (bytes
!= (off_t
)-1)
3618 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3619 " = " LLF
" byte%s", (LLT
) bytes
, PLURAL(bytes
));
3620 else if (r2
!= NULL
)
3621 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3623 if (elapsed
!= NULL
)
3624 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3625 " in " LLF
".%.03ld seconds",
3626 (LLT
)elapsed
->tv_sec
,
3627 (long)(elapsed
->tv_usec
/ 1000));
3629 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3631 syslog(LOG_INFO
, "%s", buf
);
3635 * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3637 if (!doxferlog
|| bytes
== -1)
3640 if (strcmp(command
, "get") == 0)
3642 else if (strcmp(command
, "put") == 0 || strcmp(command
, "append") == 0)
3648 len
= snprintf(buf
, sizeof(buf
),
3649 "%.24s " LLF
" %s " LLF
" %s %c %s %c %c %s FTP 0 * %c\n",
3652 * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
3653 * the full date. This may be problematic for accurate log parsing,
3654 * given that syslog messages don't contain the full date.
3658 (elapsed
== NULL
? 0 : elapsed
->tv_sec
+ (elapsed
->tv_usec
> 0)),
3662 type
== TYPE_A
? 'a' : 'b',
3663 "_", /* XXX: take conversions into account? */
3666 curclass
.type
== CLASS_GUEST
? 'a' :
3667 curclass
.type
== CLASS_CHROOT
? 'g' :
3668 curclass
.type
== CLASS_REAL
? 'r' : '?',
3670 curclass
.type
== CLASS_GUEST
? pw
->pw_passwd
: pw
->pw_name
,
3671 error
!= NULL
? 'i' : 'c'
3674 if ((doxferlog
& 2) && xferlogfd
!= -1)
3675 write(xferlogfd
, buf
, len
);
3676 if ((doxferlog
& 1)) {
3677 buf
[len
-1] = '\n'; /* strip \n from syslog message */
3678 syslog(LOG_INFO
, "xferlog: %s", buf
);
3683 * Log the resource usage.
3685 * XXX: more resource usage to logging?
3688 logrusage(const struct rusage
*rusage_before
,
3689 const struct rusage
*rusage_after
)
3691 struct timeval usrtime
, systime
;
3696 timersub(&rusage_after
->ru_utime
, &rusage_before
->ru_utime
, &usrtime
);
3697 timersub(&rusage_after
->ru_stime
, &rusage_before
->ru_stime
, &systime
);
3698 syslog(LOG_INFO
, LLF
".%.03ldu " LLF
".%.03lds %ld+%ldio %ldpf+%ldw",
3699 (LLT
)usrtime
.tv_sec
, (long)(usrtime
.tv_usec
/ 1000),
3700 (LLT
)systime
.tv_sec
, (long)(systime
.tv_usec
/ 1000),
3701 rusage_after
->ru_inblock
- rusage_before
->ru_inblock
,
3702 rusage_after
->ru_oublock
- rusage_before
->ru_oublock
,
3703 rusage_after
->ru_majflt
- rusage_before
->ru_majflt
,
3704 rusage_after
->ru_nswap
- rusage_before
->ru_nswap
);
3708 * Determine if `password' is valid for user given in `pw'.
3709 * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
3712 checkpassword(const struct passwd
*pwent
, const char *password
)
3716 time_t change
, expire
, now
;
3718 change
= expire
= 0;
3723 orig
= pwent
->pw_passwd
; /* save existing password */
3724 expire
= pwent
->pw_expire
;
3725 change
= pwent
->pw_change
;
3726 if (change
== _PASSWORD_CHGNOW
)
3729 if (orig
[0] == '\0') /* don't allow empty passwords */
3732 new = crypt(password
, orig
); /* encrypt given password */
3733 if (strcmp(new, orig
) != 0) /* compare */
3736 if ((expire
&& now
>= expire
) || (change
&& now
>= change
))
3737 return 2; /* check if expired */
3743 ftpd_strdup(const char *s
)
3745 char *new = strdup(s
);
3748 fatal("Local resource failure: malloc");
3754 * As per fprintf(), but increment total_bytes and total_bytes_out,
3755 * by the appropriate amount.
3758 cprintf(FILE *fd
, const char *fmt
, ...)
3764 b
= vfprintf(fd
, fmt
, ap
);
3767 total_bytes_out
+= b
;
3772 * the following code is stolen from imap-uw PAM authentication module and
3776 const char *uname
; /* user name */
3777 int triedonce
; /* if non-zero, tried before */
3781 auth_conv(int num_msg
, const struct pam_message
**msg
,
3782 struct pam_response
**resp
, void *appdata
)
3786 ftpd_cred_t
*cred
= (ftpd_cred_t
*) appdata
;
3787 struct pam_response
*myreply
;
3788 char pbuf
[FTP_BUFLEN
];
3790 if (num_msg
<= 0 || num_msg
> PAM_MAX_NUM_MSG
)
3791 return (PAM_CONV_ERR
);
3792 myreply
= calloc(num_msg
, sizeof *myreply
);
3793 if (myreply
== NULL
)
3796 for (i
= 0; i
< num_msg
; i
++) {
3797 myreply
[i
].resp_retcode
= 0;
3798 myreply
[i
].resp
= NULL
;
3799 switch (msg
[i
]->msg_style
) {
3800 case PAM_PROMPT_ECHO_ON
: /* user */
3801 myreply
[i
].resp
= ftpd_strdup(cred
->uname
);
3802 /* PAM frees resp. */
3804 case PAM_PROMPT_ECHO_OFF
: /* authtok (password) */
3806 * Only send a single 331 reply and
3807 * then expect a PASS.
3809 if (cred
->triedonce
) {
3811 "auth_conv: already performed PAM_PROMPT_ECHO_OFF");
3815 if (msg
[i
]->msg
[0] == '\0') {
3816 (void)strlcpy(pbuf
, "password", sizeof(pbuf
));
3818 /* Uncapitalize msg */
3819 (void)strlcpy(pbuf
, msg
[i
]->msg
, sizeof(pbuf
));
3820 if (isupper((unsigned char)pbuf
[0]))
3822 (unsigned char)pbuf
[0]);
3823 /* Remove trailing ':' and whitespace */
3826 if (isspace((unsigned char)pbuf
[n
]) ||
3833 /* Send reply, wait for a response. */
3834 reply(331, "User %s accepted, provide %s.",
3836 (void) alarm(curclass
.timeout
);
3837 ret
= get_line(pbuf
, sizeof(pbuf
)-1, stdin
);
3840 reply(221, "You could at least say goodbye.");
3842 } else if (ret
== -2) {
3843 /* XXX: should we do this reply(-530, ..) ? */
3844 reply(-530, "Command too long.");
3847 /* Ensure it is PASS */
3848 if (strncasecmp(pbuf
, "PASS ", 5) != 0) {
3850 "auth_conv: unexpected reply '%.4s'", pbuf
);
3851 /* XXX: should we do this reply(-530, ..) ? */
3852 reply(-530, "Unexpected reply '%.4s'.", pbuf
);
3855 /* Strip CRLF from "PASS" reply */
3858 (pbuf
[n
] == '\r' || pbuf
[n
] == '\n'))
3860 /* Copy password into reply */
3861 myreply
[i
].resp
= ftpd_strdup(pbuf
+5);
3862 /* PAM frees resp. */
3867 default: /* unknown message style */
3878 return PAM_CONV_ERR
;
3882 * Attempt to authenticate the user using PAM. Returns 0 if the user is
3883 * authenticated, or 1 if not authenticated. If some sort of PAM system
3884 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3885 * function returns -1. This can be used as an indication that we should
3886 * fall back to a different authentication mechanism.
3887 * pw maybe be updated to a new user if PAM_USER changes from curname.
3892 const char *tmpl_user
;
3896 ftpd_cred_t auth_cred
= { curname
, 0 };
3897 struct pam_conv conv
= { &auth_conv
, &auth_cred
};
3899 e
= pam_start("ftpd", curname
, &conv
, &pamh
);
3900 if (e
!= PAM_SUCCESS
) {
3902 * In OpenPAM, it's OK to pass NULL to pam_strerror()
3903 * if context creation has failed in the first place.
3905 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(NULL
, e
));
3909 e
= pam_set_item(pamh
, PAM_RHOST
, remotehost
);
3910 if (e
!= PAM_SUCCESS
) {
3911 syslog(LOG_ERR
, "pam_set_item(PAM_RHOST): %s",
3912 pam_strerror(pamh
, e
));
3913 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3914 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3920 e
= pam_set_item(pamh
, PAM_SOCKADDR
, &his_addr
);
3921 if (e
!= PAM_SUCCESS
) {
3922 syslog(LOG_ERR
, "pam_set_item(PAM_SOCKADDR): %s",
3923 pam_strerror(pamh
, e
));
3924 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3925 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3931 e
= pam_authenticate(pamh
, 0);
3933 syslog(LOG_DEBUG
, "pam_authenticate: user '%s' returned %d",
3938 * With PAM we support the concept of a "template"
3939 * user. The user enters a login name which is
3940 * authenticated by PAM, usually via a remote service
3941 * such as RADIUS or TACACS+. If authentication
3942 * succeeds, a different but related "template" name
3943 * is used for setting the credentials, shell, and
3944 * home directory. The name the user enters need only
3945 * exist on the remote authentication server, but the
3946 * template name must be present in the local password
3949 * This is supported by two various mechanisms in the
3950 * individual modules. However, from the application's
3951 * point of view, the template user is always passed
3952 * back as a changed value of the PAM_USER item.
3954 if ((e
= pam_get_item(pamh
, PAM_USER
, &item
)) ==
3956 tmpl_user
= (const char *) item
;
3958 || strcmp(pw
->pw_name
, tmpl_user
) != 0) {
3959 pw
= sgetpwnam(tmpl_user
);
3962 "auth_pam: PAM changed "
3963 "user from '%s' to '%s'",
3964 curname
, pw
->pw_name
);
3965 (void)strlcpy(curname
, pw
->pw_name
,
3969 syslog(LOG_ERR
, "Couldn't get PAM_USER: %s",
3970 pam_strerror(pamh
, e
));
3975 case PAM_USER_UNKNOWN
:
3981 syslog(LOG_ERR
, "pam_authenticate: %s", pam_strerror(pamh
, e
));
3987 e
= pam_acct_mgmt(pamh
, 0);
3988 if (e
!= PAM_SUCCESS
) {
3989 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
3990 pam_strerror(pamh
, e
));
3996 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3997 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
4004 #endif /* USE_PAM */