1 /* $NetBSD: ftpd.c,v 1.193 2009/03/15 07:48:36 lukem 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.193 2009/03/15 07:48:36 lukem 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 static void lostconn(int);
253 static void toolong(int);
254 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 static enum send_status
265 send_data_with_mmap(int, int, const struct stat
*, int);
266 static void logrusage(const struct rusage
*, const struct rusage
*);
267 static void logout_utmp(void);
269 int main(int, char *[]);
271 #if defined(KERBEROS)
272 int klogin(struct passwd
*, char *, char *, char *);
275 #if defined(KERBEROS5)
276 int k5login(struct passwd
*, char *, char *, char *);
277 void k5destroy(void);
281 main(int argc
, char *argv
[])
283 int ch
, on
= 1, tos
, keepalive
;
286 krb5_error_code kerror
;
289 const char *xferlogname
= NULL
;
292 sa_family_t af
= AF_UNSPEC
;
301 dopidfile
= 1; /* default: DO use a pid file to count users */
302 doutmp
= 0; /* default: Do NOT log to utmp */
303 dowtmp
= 1; /* default: DO log to wtmp */
304 doxferlog
= 0; /* default: Do NOT syslog xferlog */
305 xferlogfd
= -1; /* default: Do NOT write xferlog file */
306 getnameopts
= 0; /* default: xlate addrs to name */
315 version
= FTPD_VERSION
;
318 * LOG_NDELAY sets up the logging connection immediately,
319 * necessary for anonymous ftp's that chroot and can't do it later.
321 openlog("ftpd", LOG_PID
| LOG_NDELAY
, LOG_FTP
);
323 while ((ch
= getopt(argc
, argv
,
324 "46a:c:C:Dde:h:HlL:nP:qQrst:T:uUvV:wWX")) != -1) {
343 if ((p
= strchr(optarg
, '@')) != NULL
) {
345 strlcpy(remotehost
, p
, MAXHOSTNAMELEN
+ 1);
346 if (inet_pton(AF_INET
, p
,
347 &his_addr
.su_addr
) == 1) {
348 his_addr
.su_family
= AF_INET
;
350 sizeof(his_addr
.si_su
.su_sin
);
352 } else if (inet_pton(AF_INET6
, p
,
353 &his_addr
.su_6addr
) == 1) {
354 his_addr
.su_family
= AF_INET6
;
356 sizeof(his_addr
.si_su
.su_sin6
);
359 his_addr
.su_family
= AF_UNSPEC
;
361 pw
= sgetpwnam(optarg
);
362 exit(checkaccess(optarg
) ? 0 : 1);
370 case 'v': /* deprecated */
379 strlcpy(hostname
, optarg
, sizeof(hostname
));
383 if (gethostname(hostname
, sizeof(hostname
)) == -1)
385 hostname
[sizeof(hostname
) - 1] = '\0';
389 logging
++; /* > 1 == extra logging */
393 xferlogname
= optarg
;
397 getnameopts
= NI_NUMERICHOST
;
403 l
= strtol(optarg
, &p
, 10);
404 if (errno
|| *optarg
== '\0' || *p
!= '\0' ||
405 l
< IPPORT_RESERVED
||
406 l
> IPPORT_ANONMAX
) {
407 syslog(LOG_WARNING
, "Invalid dataport %s",
433 "-%c has been deprecated in favour of ftpd.conf",
446 if (EMPTYSTR(optarg
) || strcmp(optarg
, "-") == 0)
449 version
= ftpd_strdup(optarg
);
465 if (optopt
== 'a' || optopt
== 'C')
467 syslog(LOG_WARNING
, "unknown flag -%c ignored", optopt
);
471 if (EMPTYSTR(confdir
))
472 confdir
= _DEFAULT_CONFDIR
;
483 l
= sysconf(_SC_LOGIN_NAME_MAX
);
484 if (l
== -1 && errno
!= 0) {
485 syslog(LOG_ERR
, "sysconf _SC_LOGIN_NAME_MAX: %m");
488 syslog(LOG_WARNING
, "using conservative LOGIN_NAME_MAX value");
489 curname_len
= _POSIX_LOGIN_NAME_MAX
;
491 curname_len
= (size_t)l
;
492 curname
= malloc(curname_len
);
493 if (curname
== NULL
) {
494 syslog(LOG_ERR
, "malloc: %m");
500 int error
, fd
, i
, n
, *socks
;
502 struct addrinfo hints
, *res
, *res0
;
504 if (daemon(1, 0) == -1) {
505 syslog(LOG_ERR
, "failed to daemonize: %m");
508 (void)memset(&sa
, 0, sizeof(sa
));
509 sa
.sa_handler
= SIG_IGN
;
510 sa
.sa_flags
= SA_NOCLDWAIT
;
511 sigemptyset(&sa
.sa_mask
);
512 (void)sigaction(SIGCHLD
, &sa
, NULL
);
514 (void)memset(&hints
, 0, sizeof(hints
));
515 hints
.ai_flags
= AI_PASSIVE
;
516 hints
.ai_family
= af
;
517 hints
.ai_socktype
= SOCK_STREAM
;
518 error
= getaddrinfo(NULL
, "ftp", &hints
, &res0
);
520 syslog(LOG_ERR
, "getaddrinfo: %s", gai_strerror(error
));
524 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
)
527 syslog(LOG_ERR
, "no addresses available");
530 socks
= malloc(n
* sizeof(int));
531 fds
= malloc(n
* sizeof(struct pollfd
));
532 if (socks
== NULL
|| fds
== NULL
) {
533 syslog(LOG_ERR
, "malloc: %m");
537 for (n
= 0, res
= res0
; res
!= NULL
; res
= res
->ai_next
) {
538 socks
[n
] = socket(res
->ai_family
, res
->ai_socktype
,
542 (void)setsockopt(socks
[n
], SOL_SOCKET
, SO_REUSEADDR
,
544 if (bind(socks
[n
], res
->ai_addr
, res
->ai_addrlen
)
546 (void)close(socks
[n
]);
549 if (listen(socks
[n
], 12) == -1) {
550 (void)close(socks
[n
]);
554 fds
[n
].fd
= socks
[n
];
555 fds
[n
].events
= POLLIN
;
559 syslog(LOG_ERR
, "%m");
564 if (pidfile(NULL
) == -1)
565 syslog(LOG_ERR
, "failed to write a pid file: %m");
568 if (poll(fds
, n
, INFTIM
) == -1) {
571 syslog(LOG_ERR
, "poll: %m");
574 for (i
= 0; i
< n
; i
++) {
575 if (fds
[i
].revents
& POLLIN
) {
576 fd
= accept(fds
[i
].fd
, NULL
, NULL
);
578 syslog(LOG_ERR
, "accept: %m");
583 syslog(LOG_ERR
, "fork: %m");
594 (void)dup2(fd
, STDIN_FILENO
);
595 (void)dup2(fd
, STDOUT_FILENO
);
596 (void)dup2(fd
, STDERR_FILENO
);
597 for (i
= 0; i
< n
; i
++)
598 (void)close(socks
[i
]);
601 memset((char *)&his_addr
, 0, sizeof(his_addr
));
602 addrlen
= sizeof(his_addr
.si_su
);
603 if (getpeername(0, (struct sockaddr
*)&his_addr
.si_su
, &addrlen
) < 0) {
604 syslog(LOG_ERR
, "getpeername (%s): %m",argv
[0]);
607 his_addr
.su_len
= addrlen
;
608 memset((char *)&ctrl_addr
, 0, sizeof(ctrl_addr
));
609 addrlen
= sizeof(ctrl_addr
.si_su
);
610 if (getsockname(0, (struct sockaddr
*)&ctrl_addr
, &addrlen
) < 0) {
611 syslog(LOG_ERR
, "getsockname (%s): %m",argv
[0]);
614 ctrl_addr
.su_len
= addrlen
;
616 if (his_addr
.su_family
== AF_INET6
617 && IN6_IS_ADDR_V4MAPPED(&his_addr
.su_6addr
)) {
620 * IPv4 control connection arrived to AF_INET6 socket.
621 * I hate to do this, but this is the easiest solution.
623 * The assumption is untrue on SIIT environment.
625 struct sockinet tmp_addr
;
626 const int off
= sizeof(struct in6_addr
) - sizeof(struct in_addr
);
629 memset(&his_addr
, 0, sizeof(his_addr
));
630 his_addr
.su_family
= AF_INET
;
631 his_addr
.su_len
= sizeof(his_addr
.si_su
.su_sin
);
632 memcpy(&his_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
633 sizeof(his_addr
.su_addr
));
634 his_addr
.su_port
= tmp_addr
.su_port
;
636 tmp_addr
= ctrl_addr
;
637 memset(&ctrl_addr
, 0, sizeof(ctrl_addr
));
638 ctrl_addr
.su_family
= AF_INET
;
639 ctrl_addr
.su_len
= sizeof(ctrl_addr
.si_su
.su_sin
);
640 memcpy(&ctrl_addr
.su_addr
, &tmp_addr
.su_6addr
.s6_addr
[off
],
641 sizeof(ctrl_addr
.su_addr
));
642 ctrl_addr
.su_port
= tmp_addr
.su_port
;
644 while (fgets(line
, sizeof(line
), fd
) != NULL
) {
645 if ((cp
= strchr(line
, '\n')) != NULL
)
647 reply(-530, "%s", line
);
649 (void) fflush(stdout
);
652 "Connection from IPv4 mapped address is not supported.");
661 if (!mapped
&& his_addr
.su_family
== AF_INET
) {
662 tos
= IPTOS_LOWDELAY
;
663 if (setsockopt(0, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
665 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
668 /* if the hostname hasn't been given, attempt to determine it */
669 if (hostname
[0] == '\0') {
670 if (getnameinfo((struct sockaddr
*)&ctrl_addr
.si_su
,
671 ctrl_addr
.su_len
, hostname
, sizeof(hostname
), NULL
, 0,
673 (void)gethostname(hostname
, sizeof(hostname
));
674 hostname
[sizeof(hostname
) - 1] = '\0';
677 /* set this here so klogin can use it... */
678 (void)snprintf(ttyline
, sizeof(ttyline
), "ftp%d", getpid());
680 (void) freopen(_PATH_DEVNULL
, "w", stderr
);
682 memset(&sa
, 0, sizeof(sa
));
683 sa
.sa_handler
= SIG_DFL
;
684 sa
.sa_flags
= SA_RESTART
;
685 sigemptyset(&sa
.sa_mask
);
686 (void) sigaction(SIGCHLD
, &sa
, NULL
);
688 sa
.sa_handler
= sigquit
;
689 sa
.sa_flags
= SA_RESTART
;
690 sigfillset(&sa
.sa_mask
); /* block all sigs in these handlers */
691 (void) sigaction(SIGHUP
, &sa
, NULL
);
692 (void) sigaction(SIGINT
, &sa
, NULL
);
693 (void) sigaction(SIGQUIT
, &sa
, NULL
);
694 (void) sigaction(SIGTERM
, &sa
, NULL
);
695 sa
.sa_handler
= lostconn
;
696 (void) sigaction(SIGPIPE
, &sa
, NULL
);
697 sa
.sa_handler
= toolong
;
698 (void) sigaction(SIGALRM
, &sa
, NULL
);
699 sa
.sa_handler
= sigurg
;
700 (void) sigaction(SIGURG
, &sa
, NULL
);
702 /* Try to handle urgent data inline */
704 if (setsockopt(0, SOL_SOCKET
, SO_OOBINLINE
, (char *)&on
, sizeof(on
)) < 0)
705 syslog(LOG_WARNING
, "setsockopt: %m");
707 /* Set keepalives on the socket to detect dropped connections. */
710 if (setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&keepalive
,
712 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
716 if (fcntl(fileno(stdin
), F_SETOWN
, getpid()) == -1)
717 syslog(LOG_WARNING
, "fcntl F_SETOWN: %m");
719 logremotehost(&his_addr
);
721 * Set up default state
732 kerror
= krb5_init_context(&kcontext
);
734 syslog(LOG_ERR
, "%s when initializing Kerberos context",
735 error_message(kerror
));
738 #endif /* KERBEROS5 */
741 curclass
.timeout
= 300; /* 5 minutes, as per login(1) */
742 curclass
.type
= CLASS_REAL
;
744 /* If logins are disabled, print out the message. */
745 if (display_file(_PATH_NOLOGIN
, 530)) {
746 reply(530, "System not available.");
749 (void)display_file(conffilename(_NAME_FTPWELCOME
), 220);
750 /* reply(220,) must follow */
751 if (EMPTYSTR(version
))
752 reply(220, "%s FTP server ready.", hostname
);
754 reply(220, "%s FTP server (%s) ready.", hostname
, version
);
756 if (xferlogname
!= NULL
) {
757 xferlogfd
= open(xferlogname
, O_WRONLY
| O_APPEND
| O_CREAT
,
760 syslog(LOG_WARNING
, "open xferlog `%s': %m",
771 lostconn(int signo __unused
)
775 syslog(LOG_DEBUG
, "lost connection");
780 toolong(int signo __unused
)
785 "Timeout (" LLF
" seconds): closing control connection.",
786 (LLT
)curclass
.timeout
);
788 syslog(LOG_INFO
, "User %s timed out after " LLF
" seconds",
789 (pw
? pw
->pw_name
: "unknown"), (LLT
)curclass
.timeout
);
798 syslog(LOG_DEBUG
, "got signal %d", signo
);
803 sigurg(int signo __unused
)
811 * Save the result of a getpwnam. Used for USER command, since
812 * the data returned must not be clobbered by any other command
815 static struct passwd
*
816 sgetpwnam(const char *name
)
818 static struct passwd save
;
821 if ((p
= getpwnam(name
)) == NULL
)
824 free((char *)save
.pw_name
);
825 memset(save
.pw_passwd
, 0, strlen(save
.pw_passwd
));
826 free((char *)save
.pw_passwd
);
827 free((char *)save
.pw_gecos
);
828 free((char *)save
.pw_dir
);
829 free((char *)save
.pw_shell
);
832 save
.pw_name
= ftpd_strdup(p
->pw_name
);
833 save
.pw_passwd
= ftpd_strdup(p
->pw_passwd
);
834 save
.pw_gecos
= ftpd_strdup(p
->pw_gecos
);
835 save
.pw_dir
= ftpd_strdup(p
->pw_dir
);
836 save
.pw_shell
= ftpd_strdup(p
->pw_shell
);
840 static int login_attempts
; /* number of failed login attempts */
841 static int askpasswd
; /* had USER command, ask for PASSwd */
842 static int permitted
; /* USER permitted */
846 * Sets global passwd pointer pw if named account exists and is acceptable;
847 * sets askpasswd if a PASS command is expected. If logged in previously,
848 * need to reset state. If name is "ftp" or "anonymous", the name is not in
849 * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
850 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
851 * requesting login privileges. Disallow anyone who does not have a standard
852 * shell as returned by getusershell(). Disallow anyone mentioned in the file
853 * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
856 user(const char *name
)
860 login_cap_t
*lc
= NULL
;
868 switch (curclass
.type
) {
870 reply(530, "Can't change user from guest login.");
873 reply(530, "Can't change user from chroot user.");
877 reply(530, "Can't change user.");
887 #if defined(KERBEROS)
890 #if defined(KERBEROS5)
894 curclass
.type
= CLASS_REAL
;
898 if (strcmp(name
, "ftp") == 0 || strcmp(name
, "anonymous") == 0) {
899 /* need `pw' setup for checkaccess() and checkuser () */
900 if ((pw
= sgetpwnam("ftp")) == NULL
)
901 reply(530, "User %s unknown.", name
);
902 else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
903 reply(530, "User %s access denied.", name
);
905 curclass
.type
= CLASS_GUEST
;
908 "Guest login ok, type your name as password.");
913 "ANONYMOUS FTP LOGIN REFUSED FROM %s",
920 pw
= sgetpwnam(name
);
922 strlcpy(curname
, name
, curname_len
);
924 /* check user in /etc/ftpusers, and setup class */
925 permitted
= checkuser(_NAME_FTPUSERS
, curname
, 1, 0, &class);
927 /* check user in /etc/ftpchroot */
929 lc
= login_getpwclass(pw
);
931 if (checkuser(_NAME_FTPCHROOT
, curname
, 0, 0, NULL
)
932 #ifdef LOGIN_CAP /* Allow login.conf configuration as well */
933 || login_getcapbool(lc
, "ftp-chroot", 0)
936 if (curclass
.type
== CLASS_GUEST
) {
938 "Can't change guest user to chroot class; remove entry in %s",
942 curclass
.type
= CLASS_CHROOT
;
945 /* determine default class */
947 switch (curclass
.type
) {
949 class = ftpd_strdup("guest");
952 class = ftpd_strdup("chroot");
955 class = ftpd_strdup("real");
958 syslog(LOG_ERR
, "unknown curclass.type %d; aborting",
963 /* parse ftpd.conf, setting up various parameters */
965 /* if not guest user, check for valid shell */
969 const char *cp
, *shell
;
971 if ((shell
= pw
->pw_shell
) == NULL
|| *shell
== 0)
972 shell
= _PATH_BSHELL
;
973 while ((cp
= getusershell()) != NULL
)
974 if (strcmp(cp
, shell
) == 0)
977 if (cp
== NULL
&& curclass
.type
!= CLASS_GUEST
)
981 /* deny quickly (after USER not PASS) if requested */
982 if (CURCLASS_FLAGS_ISSET(denyquick
) && !permitted
) {
983 reply(530, "User %s may not use FTP.", curname
);
985 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
986 remoteloghost
, curname
);
991 /* if haven't asked yet (i.e, not anon), ask now */
995 e
= auth_pam(); /* this does reply(331, ...) */
1000 if (skey_haskey(curname
) == 0) {
1003 myskey
= skey_keyinfo(curname
);
1004 reply(331, "Password [ %s ] required for %s.",
1005 myskey
? myskey
: "error getting challenge",
1009 reply(331, "Password required for %s.", curname
);
1010 #endif /* !USE_PAM */
1018 * Delay before reading passwd after first failed
1019 * attempt to slow down passwd-guessing programs.
1022 sleep((unsigned) login_attempts
);
1029 * Determine whether something is to happen (allow access, chroot)
1030 * for a user. Each line is a shell-style glob followed by
1033 * For backward compatibility, `allow' and `deny' are synonymns
1034 * for `yes' and `no', respectively.
1036 * Each glob is matched against the username in turn, and the first
1037 * match found is used. If no match is found, the result is the
1038 * argument `def'. If a match is found but without and explicit
1039 * `yes'/`no', the result is the opposite of def.
1041 * If the file doesn't exist at all, the result is the argument
1044 * Any line starting with `#' is considered a comment and ignored.
1046 * Returns 0 if the user is denied, or 1 if they are allowed.
1048 * NOTE: needs struct passwd *pw setup before use.
1051 checkuser(const char *fname
, const char *name
, int def
, int nofile
,
1056 char *word
, *perm
, *class, *buf
, *p
;
1060 if (retclass
!= NULL
)
1062 if ((fd
= fopen(conffilename(fname
), "r")) == NULL
)
1067 (buf
= fparseln(fd
, &len
, &line
, NULL
, FPARSELN_UNESCCOMM
|
1068 FPARSELN_UNESCCONT
| FPARSELN_UNESCESC
)) != NULL
;
1069 free(buf
), buf
= NULL
) {
1070 word
= perm
= class = NULL
;
1074 if (p
[len
- 1] == '\n')
1084 if (!EMPTYSTR(class)) {
1085 if (strcasecmp(class, "all") == 0 ||
1086 strcasecmp(class, "none") == 0) {
1088 "%s line %d: illegal user-defined class `%s' - skipping entry",
1089 fname
, (int)line
, class);
1094 /* have a host specifier */
1095 if ((p
= strchr(word
, '@')) != NULL
) {
1096 unsigned char net
[16], mask
[16], *addr
;
1097 int addrlen
, bits
, bytes
, a
;
1100 /* check against network or CIDR */
1101 memset(net
, 0x00, sizeof(net
));
1102 if ((bits
= inet_net_pton(his_addr
.su_family
, p
, net
,
1103 sizeof(net
))) != -1) {
1105 if (his_addr
.su_family
== AF_INET
) {
1108 addr
= (unsigned char *)&his_addr
.su_addr
;
1112 addr
= (unsigned char *)&his_addr
.su_6addr
;
1118 memset(mask
, 0xFF, bytes
);
1119 if (bytes
< addrlen
)
1120 mask
[bytes
] = 0xFF << (8 - bits
);
1121 if (bytes
+ 1 < addrlen
)
1122 memset(mask
+ bytes
+ 1, 0x00,
1123 addrlen
- bytes
- 1);
1124 for (a
= 0; a
< addrlen
; a
++)
1125 if ((addr
[a
] & mask
[a
]) != net
[a
])
1130 /* check against hostname glob */
1131 } else if (fnmatch(p
, remotehost
, FNM_CASEFOLD
) != 0)
1135 /* have a group specifier */
1136 if ((p
= strchr(word
, ':')) != NULL
) {
1138 int gsize
, i
, found
;
1141 continue; /* no match for unknown user */
1146 ng
= realloc(groups
, gsize
* sizeof(gid_t
));
1149 "Local resource failure: realloc");
1151 } while (getgrouplist(pw
->pw_name
, pw
->pw_gid
,
1152 groups
, &gsize
) == -1);
1154 for (i
= 0; i
< gsize
; i
++) {
1157 if ((g
= getgrgid(groups
[i
])) == NULL
)
1159 if (fnmatch(p
, g
->gr_name
, 0) == 0) {
1169 /* check against username glob */
1170 if (fnmatch(word
, name
, 0) != 0)
1174 ((strcasecmp(perm
, "allow") == 0) ||
1175 (strcasecmp(perm
, "yes") == 0)))
1177 else if (perm
!= NULL
&&
1178 ((strcasecmp(perm
, "deny") == 0) ||
1179 (strcasecmp(perm
, "no") == 0)))
1183 if (!EMPTYSTR(class) && retclass
!= NULL
)
1184 *retclass
= ftpd_strdup(class);
1193 * Check if user is allowed by /etc/ftpusers
1194 * returns 1 for yes, 0 for no
1196 * NOTE: needs struct passwd *pw setup (for checkuser())
1199 checkaccess(const char *name
)
1202 return (checkuser(_NAME_FTPUSERS
, name
, 1, 0, NULL
));
1206 login_utmp(const char *line
, const char *name
, const char *host
,
1207 struct sockinet
*haddr
)
1209 #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1211 (void)gettimeofday(&tv
, NULL
);
1213 #ifdef SUPPORT_UTMPX
1215 (void)memset(&utmpx
, 0, sizeof(utmpx
));
1217 utmpx
.ut_pid
= getpid();
1218 utmpx
.ut_id
[0] = 'f';
1219 utmpx
.ut_id
[1] = 't';
1220 utmpx
.ut_id
[2] = 'p';
1221 utmpx
.ut_id
[3] = '*';
1222 utmpx
.ut_type
= USER_PROCESS
;
1223 (void)strncpy(utmpx
.ut_name
, name
, sizeof(utmpx
.ut_name
));
1224 (void)strncpy(utmpx
.ut_line
, line
, sizeof(utmpx
.ut_line
));
1225 (void)strncpy(utmpx
.ut_host
, host
, sizeof(utmpx
.ut_host
));
1226 (void)memcpy(&utmpx
.ut_ss
, &haddr
->si_su
, haddr
->su_len
);
1227 ftpd_loginx(&utmpx
);
1230 ftpd_logwtmpx(line
, name
, host
, haddr
, 0, USER_PROCESS
);
1234 (void)memset(&utmp
, 0, sizeof(utmp
));
1235 (void)time(&utmp
.ut_time
);
1236 (void)strncpy(utmp
.ut_name
, name
, sizeof(utmp
.ut_name
));
1237 (void)strncpy(utmp
.ut_line
, line
, sizeof(utmp
.ut_line
));
1238 (void)strncpy(utmp
.ut_host
, host
, sizeof(utmp
.ut_host
));
1242 ftpd_logwtmp(line
, name
, host
);
1249 #ifdef SUPPORT_UTMPX
1250 int okwtmpx
= dowtmp
;
1253 int okwtmp
= dowtmp
;
1256 #ifdef SUPPORT_UTMPX
1258 okwtmpx
&= ftpd_logoutx(ttyline
, 0, DEAD_PROCESS
);
1260 ftpd_logwtmpx(ttyline
, "", "", NULL
, 0, DEAD_PROCESS
);
1264 okwtmp
&= ftpd_logout(ttyline
);
1266 ftpd_logwtmp(ttyline
, "", "");
1272 * Terminate login as previous user (if any), resetting state;
1273 * used when USER command is given or login fails.
1282 show_chdir_messages(-1); /* flush chdir cache */
1283 if (pw
!= NULL
&& pw
->pw_passwd
!= NULL
)
1284 memset(pw
->pw_passwd
, 0, strlen(pw
->pw_passwd
));
1291 curclass
.type
= CLASS_REAL
;
1292 (void) seteuid((uid_t
)0);
1294 setusercontext(NULL
, getpwuid(0), 0,
1295 LOGIN_SETPRIORITY
|LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1299 if ((e
= pam_setcred(pamh
, PAM_DELETE_CRED
)) != PAM_SUCCESS
)
1300 syslog(LOG_ERR
, "pam_setcred: %s",
1301 pam_strerror(pamh
, e
));
1302 if ((e
= pam_close_session(pamh
,0)) != PAM_SUCCESS
)
1303 syslog(LOG_ERR
, "pam_close_session: %s",
1304 pam_strerror(pamh
, e
));
1305 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
)
1306 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
1313 pass(const char *passwd
)
1315 do_pass(0, 0, passwd
);
1319 * Perform the passwd confirmation and login.
1321 * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval.
1322 * This is the traditional PASS implementation.
1324 * If pass_checked is non-zero, use pass_rval and ignore passwd.
1325 * This is used by auth_pam() which has already parsed PASS.
1326 * This only applies to curclass.type != CLASS_GUEST.
1329 do_pass(int pass_checked
, int pass_rval
, const char *passwd
)
1332 char root
[MAXPATHLEN
];
1334 login_cap_t
*lc
= NULL
;
1342 if (logged_in
|| askpasswd
== 0) {
1343 reply(503, "Login with USER first.");
1347 if (curclass
.type
!= CLASS_GUEST
) {
1348 /* "ftp" is the only account allowed with no password */
1350 rval
= 1; /* failure below */
1353 if (pass_checked
) { /* password validated in user() */
1358 syslog(LOG_ERR
, "do_pass: USE_PAM shouldn't get here");
1362 #if defined(KERBEROS)
1363 if (klogin(pw
, "", hostname
, (char *)passwd
) == 0) {
1368 #if defined(KERBEROS5)
1369 if (k5login(pw
, "", hostname
, (char *)passwd
) == 0) {
1375 if (skey_haskey(pw
->pw_name
) == 0) {
1379 p
= ftpd_strdup(passwd
);
1380 r
= skey_passcheck(pw
->pw_name
, p
);
1389 rval
= checkpassword(pw
, passwd
);
1396 * If rval > 0, the user failed the authentication check
1397 * above. If rval == 0, either Kerberos or local
1398 * authentication succeeded.
1401 reply(530, "%s", rval
== 2 ? "Password expired." :
1402 "Login incorrect.");
1405 "FTP LOGIN FAILED FROM %s", remoteloghost
);
1406 syslog(LOG_AUTHPRIV
| LOG_NOTICE
,
1407 "FTP LOGIN FAILED FROM %s, %s",
1408 remoteloghost
, curname
);
1411 if (login_attempts
++ >= 5) {
1413 "repeated login failures from %s",
1421 /* password ok; check if anything else prevents login */
1423 reply(530, "User %s may not use FTP.", pw
->pw_name
);
1425 syslog(LOG_NOTICE
, "FTP LOGIN REFUSED FROM %s, %s",
1426 remoteloghost
, pw
->pw_name
);
1430 login_attempts
= 0; /* this time successful */
1431 if (setegid((gid_t
)pw
->pw_gid
) < 0) {
1432 reply(550, "Can't set gid.");
1436 if ((lc
= login_getpwclass(pw
)) != NULL
) {
1438 char remote_ip
[NI_MAXHOST
];
1440 if (getnameinfo((struct sockaddr
*)&his_addr
, his_addr
.su_len
,
1441 remote_ip
, sizeof(remote_ip
) - 1, NULL
, 0,
1444 remote_ip
[sizeof(remote_ip
) - 1] = 0;
1445 if (!auth_hostok(lc
, remotehost
, remote_ip
)) {
1446 syslog(LOG_INFO
|LOG_AUTH
,
1447 "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1449 reply(530, "Permission denied.");
1453 if (!auth_timeok(lc
, time(NULL
))) {
1454 reply(530, "Login not available right now.");
1461 setusercontext(lc
, pw
, 0,
1462 LOGIN_SETLOGIN
|LOGIN_SETGROUP
|LOGIN_SETPRIORITY
|
1463 LOGIN_SETRESOURCES
|LOGIN_SETUMASK
);
1465 (void) initgroups(pw
->pw_name
, pw
->pw_gid
);
1466 /* cache groups for cmds.c::matchgroup() */
1470 if ((e
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
1471 syslog(LOG_ERR
, "pam_open_session: %s",
1472 pam_strerror(pamh
, e
));
1473 } else if ((e
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
))
1475 syslog(LOG_ERR
, "pam_setcred: %s",
1476 pam_strerror(pamh
, e
));
1480 gidcount
= getgroups(0, NULL
);
1483 gidlist
= malloc(gidcount
* sizeof *gidlist
);
1484 gidcount
= getgroups(gidcount
, gidlist
);
1486 /* open utmp/wtmp before chroot */
1487 login_utmp(ttyline
, pw
->pw_name
, remotehost
, &his_addr
);
1494 if (curclass
.limit
!= -1 && connections
> curclass
.limit
) {
1495 if (! EMPTYSTR(curclass
.limitfile
))
1496 (void)display_file(conffilename(curclass
.limitfile
),
1499 "User %s access denied, connection limit of " LLF
1501 pw
->pw_name
, (LLT
)curclass
.limit
);
1503 "Maximum connection limit of " LLF
1504 " for class %s reached, login refused for %s",
1505 (LLT
)curclass
.limit
, curclass
.classname
, pw
->pw_name
);
1510 switch (curclass
.type
) {
1513 * We MUST do a chdir() after the chroot. Otherwise
1514 * the old current directory will be accessible as "."
1515 * outside the new root!
1518 curclass
.chroot
? curclass
.chroot
:
1521 format_path(homedir
,
1522 curclass
.homedir
? curclass
.homedir
:
1524 if (EMPTYSTR(homedir
))
1526 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1528 "GUEST user %s: can't chroot to %s: %m",
1532 if (chdir(homedir
) < 0) {
1534 "GUEST user %s: can't chdir to %s: %m",
1535 pw
->pw_name
, homedir
);
1537 reply(550, "Can't set guest privileges.");
1543 curclass
.chroot
? curclass
.chroot
:
1545 format_path(homedir
,
1546 curclass
.homedir
? curclass
.homedir
:
1548 if (EMPTYSTR(homedir
))
1550 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1552 "CHROOT user %s: can't chroot to %s: %m",
1556 if (chdir(homedir
) < 0) {
1558 "CHROOT user %s: can't chdir to %s: %m",
1559 pw
->pw_name
, homedir
);
1561 reply(550, "Can't change root.");
1566 /* only chroot REAL if explicitly requested */
1567 if (! EMPTYSTR(curclass
.chroot
)) {
1568 format_path(root
, curclass
.chroot
);
1569 if (EMPTYSTR(root
) || chroot(root
) < 0) {
1571 "REAL user %s: can't chroot to %s: %m",
1576 format_path(homedir
,
1577 curclass
.homedir
? curclass
.homedir
:
1579 if (EMPTYSTR(homedir
) || chdir(homedir
) < 0) {
1580 if (chdir("/") < 0) {
1582 "REAL user %s: can't chdir to %s: %m",
1584 !EMPTYSTR(homedir
) ? homedir
: "/");
1586 "User %s: can't change directory to %s.",
1588 !EMPTYSTR(homedir
) ? homedir
: "/");
1592 "No directory! Logging in with home=/");
1600 setlogin(pw
->pw_name
);
1603 (curclass
.type
!= CLASS_REAL
&&
1604 ntohs(ctrl_addr
.su_port
) > IPPORT_RESERVED
+ 1)) {
1606 if (setgid((gid_t
)pw
->pw_gid
) < 0) {
1607 reply(550, "Can't set gid.");
1610 if (setuid((uid_t
)pw
->pw_uid
) < 0) {
1611 reply(550, "Can't set uid.");
1615 if (seteuid((uid_t
)pw
->pw_uid
) < 0) {
1616 reply(550, "Can't set uid.");
1620 setenv("HOME", homedir
, 1);
1622 if (curclass
.type
== CLASS_GUEST
&& passwd
[0] == '-')
1626 * Display a login message, if it exists.
1627 * N.B. reply(230,) must follow the message.
1629 if (! EMPTYSTR(curclass
.motd
))
1630 (void)display_file(conffilename(curclass
.motd
), 230);
1631 show_chdir_messages(230);
1632 if (curclass
.type
== CLASS_GUEST
) {
1635 reply(230, "Guest login ok, access restrictions apply.");
1636 #if defined(HAVE_SETPROCTITLE)
1637 snprintf(proctitle
, sizeof(proctitle
),
1638 "%s: anonymous/%s", remotehost
, passwd
);
1639 setproctitle("%s", proctitle
);
1640 #endif /* defined(HAVE_SETPROCTITLE) */
1643 "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1644 remoteloghost
, passwd
,
1645 curclass
.classname
, CURCLASSTYPE
);
1646 /* store guest password reply into pw_passwd */
1647 REASSIGN(pw
->pw_passwd
, ftpd_strdup(passwd
));
1648 for (p
= pw
->pw_passwd
; *p
; p
++)
1649 if (!isgraph((unsigned char)*p
))
1652 reply(230, "User %s logged in.", pw
->pw_name
);
1653 #if defined(HAVE_SETPROCTITLE)
1654 snprintf(proctitle
, sizeof(proctitle
),
1655 "%s: %s", remotehost
, pw
->pw_name
);
1656 setproctitle("%s", proctitle
);
1657 #endif /* defined(HAVE_SETPROCTITLE) */
1660 "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1661 remoteloghost
, pw
->pw_name
,
1662 curclass
.classname
, CURCLASSTYPE
);
1664 (void) umask(curclass
.umask
);
1674 /* Forget all about it... */
1679 retrieve(const char *argv
[], const char *name
)
1683 int (*closefunc
)(FILE *) = NULL
;
1684 int dolog
, sendrv
, closerv
, stderrfd
, isconversion
, isdata
, isls
;
1685 struct timeval start
, finish
, td
, *tdp
;
1686 struct rusage rusage_before
, rusage_after
;
1687 const char *dispname
;
1690 sendrv
= closerv
= stderrfd
= -1;
1691 isconversion
= isdata
= isls
= dolog
= 0;
1696 if (argv
== NULL
) { /* if not running a command ... */
1699 fin
= fopen(name
, "r");
1701 if (fin
== NULL
) /* doesn't exist?; try a conversion */
1702 argv
= do_conversion(name
);
1705 syslog(LOG_DEBUG
, "get command: '%s' on '%s'",
1710 char temp
[MAXPATHLEN
];
1712 if (strcmp(argv
[0], INTERNAL_LS
) == 0) {
1716 (void)snprintf(temp
, sizeof(temp
), "%s", TMPFILE
);
1717 stderrfd
= mkstemp(temp
);
1722 fin
= ftpd_popen(argv
, "r", stderrfd
);
1723 closefunc
= ftpd_pclose
;
1725 st
.st_blksize
= BUFSIZ
;
1729 perror_reply(550, dispname
);
1731 logxfer("get", -1, name
, NULL
, NULL
,
1734 goto cleanupretrieve
;
1738 && (fstat(fileno(fin
), &st
) < 0 || !S_ISREG(st
.st_mode
))) {
1739 error
= "Not a plain file";
1740 reply(550, "%s: %s.", dispname
, error
);
1743 if (restart_point
) {
1744 if (type
== TYPE_A
) {
1748 for (i
= 0; i
< restart_point
; i
++) {
1749 if ((c
=getc(fin
)) == EOF
) {
1750 error
= strerror(errno
);
1751 perror_reply(550, dispname
);
1757 } else if (lseek(fileno(fin
), restart_point
, SEEK_SET
) < 0) {
1758 error
= strerror(errno
);
1759 perror_reply(550, dispname
);
1763 dout
= dataconn(dispname
, st
.st_size
, "w");
1767 (void)getrusage(RUSAGE_SELF
, &rusage_before
);
1768 (void)gettimeofday(&start
, NULL
);
1769 sendrv
= send_data(fin
, dout
, &st
, isdata
);
1770 (void)gettimeofday(&finish
, NULL
);
1771 (void)getrusage(RUSAGE_SELF
, &rusage_after
);
1772 closedataconn(dout
); /* close now to affect timing stats */
1773 timersub(&finish
, &start
, &td
);
1777 logxfer("get", byte_count
, name
, NULL
, tdp
, error
);
1779 logrusage(&rusage_before
, &rusage_after
);
1781 closerv
= (*closefunc
)(fin
);
1786 if (!isls
&& argv
!= NULL
&& closerv
!= 0) {
1788 "Command returned an exit status of %d",
1792 "retrieve command: '%s' returned %d",
1795 if (!isls
&& argv
!= NULL
&& stderrfd
!= -1 &&
1796 (fstat(stderrfd
, &sb
) == 0) && sb
.st_size
> 0 &&
1797 ((errf
= fdopen(stderrfd
, "r")) != NULL
)) {
1798 char *cp
, line
[LINE_MAX
];
1800 reply(-226, "Command error messages:");
1802 while (fgets(line
, sizeof(line
), errf
) != NULL
) {
1803 if ((cp
= strchr(line
, '\n')) != NULL
)
1805 reply(0, " %s", line
);
1807 (void) fflush(stdout
);
1808 (void) fclose(errf
);
1809 /* a reply(226,) must follow */
1811 reply(226, "Transfer complete.");
1815 (void)close(stderrfd
);
1821 store(const char *name
, const char *fmode
, int unique
)
1825 int (*closefunc
)(FILE *);
1826 struct timeval start
, finish
, td
, *tdp
;
1827 const char *desc
, *error
;
1830 desc
= (*fmode
== 'w') ? "put" : "append";
1832 if (unique
&& stat(name
, &st
) == 0 &&
1833 (name
= gunique(name
)) == NULL
) {
1834 logxfer(desc
, -1, name
, NULL
, NULL
,
1835 "cannot create unique file");
1841 fout
= fopen(name
, fmode
);
1845 perror_reply(553, name
);
1846 logxfer(desc
, -1, name
, NULL
, NULL
, strerror(errno
));
1850 if (restart_point
) {
1851 if (type
== TYPE_A
) {
1855 for (i
= 0; i
< restart_point
; i
++) {
1856 if ((c
=getc(fout
)) == EOF
) {
1857 error
= strerror(errno
);
1858 perror_reply(550, name
);
1865 * We must do this seek to "current" position
1866 * because we are changing from reading to
1869 if (fseek(fout
, 0L, SEEK_CUR
) < 0) {
1870 error
= strerror(errno
);
1871 perror_reply(550, name
);
1874 } else if (lseek(fileno(fout
), restart_point
, SEEK_SET
) < 0) {
1875 error
= strerror(errno
);
1876 perror_reply(550, name
);
1880 din
= dataconn(name
, (off_t
)-1, "r");
1883 (void)gettimeofday(&start
, NULL
);
1884 if (receive_data(din
, fout
) == 0) {
1886 reply(226, "Transfer complete (unique file name:%s).",
1889 reply(226, "Transfer complete.");
1891 (void)gettimeofday(&finish
, NULL
);
1892 closedataconn(din
); /* close now to affect timing stats */
1893 timersub(&finish
, &start
, &td
);
1896 logxfer(desc
, byte_count
, name
, NULL
, tdp
, error
);
1903 getdatasock(const char *fmode
)
1905 int on
, s
, t
, tries
;
1910 return (fdopen(data
, fmode
));
1912 (void) seteuid((uid_t
)0);
1913 s
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
1916 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
1917 (char *) &on
, sizeof(on
)) < 0)
1919 if (setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
1920 (char *) &on
, sizeof(on
)) < 0)
1922 /* anchor socket to avoid multi-homing problems */
1923 data_source
= ctrl_addr
;
1925 * By default source port for PORT connctions is
1926 * ctrlport-1 (see RFC959 section 5.2).
1927 * However, if privs have been dropped and that
1928 * would be < IPPORT_RESERVED, use a random port
1934 port
= ntohs(ctrl_addr
.su_port
) - 1;
1935 if (dropprivs
&& port
< IPPORT_RESERVED
)
1936 port
= 0; /* use random port */
1937 data_source
.su_port
= htons(port
);
1939 for (tries
= 1; ; tries
++) {
1940 if (bind(s
, (struct sockaddr
*)&data_source
.si_su
,
1941 data_source
.su_len
) >= 0)
1943 if (errno
!= EADDRINUSE
|| tries
> 10)
1948 (void) seteuid((uid_t
)pw
->pw_uid
);
1950 if (!mapped
&& ctrl_addr
.su_family
== AF_INET
) {
1951 on
= IPTOS_THROUGHPUT
;
1952 if (setsockopt(s
, IPPROTO_IP
, IP_TOS
, (char *)&on
,
1954 syslog(LOG_WARNING
, "setsockopt (IP_TOS): %m");
1957 return (fdopen(s
, fmode
));
1959 /* Return the real value of errno (close may change it) */
1962 (void) seteuid((uid_t
)pw
->pw_uid
);
1969 dataconn(const char *name
, off_t size
, const char *fmode
)
1973 int retry
, tos
, keepalive
, conerrno
;
1977 if (size
!= (off_t
) -1)
1978 (void)snprintf(sizebuf
, sizeof(sizebuf
), " (" LLF
" byte%s)",
1979 (LLT
)size
, PLURAL(size
));
1983 struct sockinet from
;
1985 socklen_t fromlen
= sizeof(from
.su_len
);
1987 (void) alarm(curclass
.timeout
);
1988 s
= accept(pdata
, (struct sockaddr
*)&from
.si_su
, &fromlen
);
1991 reply(425, "Can't open data connection.");
1992 (void) close(pdata
);
1996 (void) close(pdata
);
1998 switch (from
.su_family
) {
2002 tos
= IPTOS_THROUGHPUT
;
2003 (void) setsockopt(s
, IPPROTO_IP
, IP_TOS
,
2004 (char *)&tos
, sizeof(int));
2009 /* Set keepalives on the socket to detect dropped conns. */
2012 (void) setsockopt(s
, SOL_SOCKET
, SO_KEEPALIVE
,
2013 (char *)&keepalive
, sizeof(int));
2015 reply(150, "Opening %s mode data connection for '%s'%s.",
2016 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2017 return (fdopen(pdata
, fmode
));
2020 reply(125, "Using existing data connection for '%s'%s.",
2023 return (fdopen(data
, fmode
));
2026 data_dest
= his_addr
;
2028 retry
= conerrno
= 0;
2030 file
= getdatasock(fmode
);
2032 char hbuf
[NI_MAXHOST
];
2033 char pbuf
[NI_MAXSERV
];
2035 if (getnameinfo((struct sockaddr
*)&data_source
.si_su
,
2036 data_source
.su_len
, hbuf
, sizeof(hbuf
), pbuf
,
2037 sizeof(pbuf
), NI_NUMERICHOST
| NI_NUMERICSERV
))
2038 strlcpy(hbuf
, "?", sizeof(hbuf
));
2039 reply(425, "Can't create data socket (%s,%s): %s.",
2040 hbuf
, pbuf
, strerror(errno
));
2043 data
= fileno(file
);
2045 if (connect(data
, (struct sockaddr
*)&data_dest
.si_su
,
2046 data_dest
.su_len
) == 0)
2049 (void) fclose(file
);
2052 if (conerrno
== EADDRINUSE
) {
2053 sleep((unsigned) swaitint
);
2058 } while (retry
<= swaitmax
);
2059 if (conerrno
!= 0) {
2060 perror_reply(425, "Can't build data connection");
2063 reply(150, "Opening %s mode data connection for '%s'%s.",
2064 type
== TYPE_A
? "ASCII" : "BINARY", name
, sizebuf
);
2069 closedataconn(FILE *fd
)
2082 write_data(int fd
, char *buf
, size_t size
, off_t
*bufrem
,
2083 struct timeval
*then
, int isdata
)
2085 struct timeval now
, td
;
2090 if (curclass
.writesize
) {
2091 if (curclass
.writesize
< c
)
2092 c
= curclass
.writesize
;
2094 if (curclass
.rateget
) {
2098 (void) alarm(curclass
.timeout
);
2099 c
= write(fd
, buf
, c
);
2106 total_data_out
+= c
;
2109 total_bytes_out
+= c
;
2111 if (curclass
.rateget
) {
2114 (void)gettimeofday(&now
, NULL
);
2115 timersub(&now
, then
, &td
);
2116 if (td
.tv_sec
== 0) {
2117 usleep(1000000 - td
.tv_usec
);
2118 (void)gettimeofday(then
, NULL
);
2121 *bufrem
= curclass
.rateget
;
2128 static enum send_status
2129 send_data_with_read(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2131 struct timeval then
;
2137 if (curclass
.readsize
> 0)
2138 readsize
= curclass
.readsize
;
2140 readsize
= st
->st_blksize
;
2141 if ((buf
= malloc(readsize
)) == NULL
) {
2142 perror_reply(451, "Local resource failure: malloc");
2143 return (SS_NO_TRANSFER
);
2146 if (curclass
.rateget
) {
2147 bufrem
= curclass
.rateget
;
2148 (void)gettimeofday(&then
, NULL
);
2152 (void) alarm(curclass
.timeout
);
2153 c
= read(filefd
, buf
, readsize
);
2157 error
= SS_FILE_ERROR
;
2158 else if (write_data(netfd
, buf
, c
, &bufrem
, &then
, isdata
))
2159 error
= SS_DATA_ERROR
;
2160 else if (urgflag
&& handleoobcmd())
2170 static enum send_status
2171 send_data_with_mmap(int filefd
, int netfd
, const struct stat
*st
, int isdata
)
2173 struct timeval then
;
2174 off_t bufrem
, filesize
, off
, origoff
;
2175 ssize_t mapsize
, winsize
;
2176 int error
, sendbufsize
, sendlowat
;
2180 if (curclass
.sendbufsize
) {
2181 sendbufsize
= curclass
.sendbufsize
;
2182 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDBUF
,
2183 &sendbufsize
, sizeof(int)) == -1)
2184 syslog(LOG_WARNING
, "setsockopt(SO_SNDBUF, %d): %m",
2188 if (curclass
.sendlowat
) {
2189 sendlowat
= curclass
.sendlowat
;
2190 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDLOWAT
,
2191 &sendlowat
, sizeof(int)) == -1)
2192 syslog(LOG_WARNING
, "setsockopt(SO_SNDLOWAT, %d): %m",
2196 winsize
= curclass
.mmapsize
;
2197 filesize
= st
->st_size
;
2199 syslog(LOG_INFO
, "mmapsize = " LLF
", writesize = " LLF
,
2200 (LLT
)winsize
, (LLT
)curclass
.writesize
);
2204 off
= lseek(filefd
, (off_t
)0, SEEK_CUR
);
2209 if (curclass
.rateget
) {
2210 bufrem
= curclass
.rateget
;
2211 (void)gettimeofday(&then
, NULL
);
2215 mapsize
= MIN(filesize
- off
, winsize
);
2218 win
= mmap(NULL
, mapsize
, PROT_READ
,
2219 MAP_FILE
|MAP_SHARED
, filefd
, off
);
2220 if (win
== MAP_FAILED
) {
2223 return (SS_FILE_ERROR
);
2225 (void) madvise(win
, mapsize
, MADV_SEQUENTIAL
);
2226 error
= write_data(netfd
, win
, mapsize
, &bufrem
, &then
,
2228 (void) madvise(win
, mapsize
, MADV_DONTNEED
);
2229 munmap(win
, mapsize
);
2230 if (urgflag
&& handleoobcmd())
2231 return (SS_ABORTED
);
2233 return (SS_DATA_ERROR
);
2236 return (SS_SUCCESS
);
2239 return (send_data_with_read(filefd
, netfd
, st
, isdata
));
2243 * Transfer the contents of "instr" to "outstr" peer using the appropriate
2244 * encapsulation of the data subject to Mode, Structure, and Type.
2246 * NB: Form isn't handled.
2249 send_data(FILE *instr
, FILE *outstr
, const struct stat
*st
, int isdata
)
2251 int c
, filefd
, netfd
, rval
;
2260 /* XXXLUKEM: rate limit ascii send (get) */
2261 (void) alarm(curclass
.timeout
);
2262 while ((c
= getc(instr
)) != EOF
) {
2263 if (urgflag
&& handleoobcmd())
2264 goto cleanup_send_data
;
2269 (void) putc('\r', outstr
);
2277 (void) putc(c
, outstr
);
2284 if ((byte_count
% 4096) == 0)
2285 (void) alarm(curclass
.timeout
);
2294 goto cleanup_send_data
;
2298 filefd
= fileno(instr
);
2299 netfd
= fileno(outstr
);
2300 switch (send_data_with_mmap(filefd
, netfd
, st
, isdata
)) {
2306 case SS_NO_TRANSFER
:
2307 goto cleanup_send_data
;
2316 goto cleanup_send_data
;
2319 reply(550, "Unimplemented TYPE %d in send_data", type
);
2320 goto cleanup_send_data
;
2325 perror_reply(426, "Data connection");
2326 goto cleanup_send_data
;
2330 perror_reply(551, "Error on input file");
2331 goto cleanup_send_data
;
2347 * Transfer data from peer to "outstr" using the appropriate encapulation of
2348 * the data subject to Mode, Structure, and Type.
2350 * N.B.: Form isn't handled.
2353 receive_data(FILE *instr
, FILE *outstr
)
2355 int c
, netfd
, filefd
, rval
;
2356 int volatile bare_lfs
;
2360 struct sigaction sa
, sa_saved
;
2363 memset(&sa
, 0, sizeof(sa
));
2364 sigfillset(&sa
.sa_mask
);
2365 sa
.sa_flags
= SA_RESTART
;
2366 sa
.sa_handler
= lostconn
;
2367 (void) sigaction(SIGALRM
, &sa
, &sa_saved
);
2376 #define FILESIZECHECK(x) \
2378 if (curclass.maxfilesize != -1 && \
2379 (x) > curclass.maxfilesize) { \
2389 netfd
= fileno(instr
);
2390 filefd
= fileno(outstr
);
2391 (void) alarm(curclass
.timeout
);
2392 if (curclass
.readsize
)
2393 readsize
= curclass
.readsize
;
2394 else if (fstat(filefd
, &st
))
2395 readsize
= (ssize_t
)st
.st_blksize
;
2398 if ((buf
= malloc(readsize
)) == NULL
) {
2399 perror_reply(451, "Local resource failure: malloc");
2400 goto cleanup_recv_data
;
2402 if (curclass
.rateput
) {
2405 struct timeval then
, now
, td
;
2408 (void)gettimeofday(&then
, NULL
);
2410 for (bufrem
= curclass
.rateput
; bufrem
> 0; ) {
2411 if ((c
= read(netfd
, buf
,
2412 MIN(readsize
, bufrem
))) <= 0)
2414 if (urgflag
&& handleoobcmd())
2415 goto cleanup_recv_data
;
2416 FILESIZECHECK(byte_count
+ c
);
2417 if ((d
= write(filefd
, buf
, c
)) != c
)
2419 (void) alarm(curclass
.timeout
);
2424 total_bytes_in
+= c
;
2427 (void)gettimeofday(&now
, NULL
);
2428 timersub(&now
, &then
, &td
);
2430 usleep(1000000 - td
.tv_usec
);
2433 while ((c
= read(netfd
, buf
, readsize
)) > 0) {
2434 if (urgflag
&& handleoobcmd())
2435 goto cleanup_recv_data
;
2436 FILESIZECHECK(byte_count
+ c
);
2437 if (write(filefd
, buf
, c
) != c
)
2439 (void) alarm(curclass
.timeout
);
2443 total_bytes_in
+= c
;
2451 goto cleanup_recv_data
;
2454 reply(553, "TYPE E not implemented.");
2455 goto cleanup_recv_data
;
2458 (void) alarm(curclass
.timeout
);
2459 /* XXXLUKEM: rate limit ascii receive (put) */
2460 while ((c
= getc(instr
)) != EOF
) {
2461 if (urgflag
&& handleoobcmd())
2462 goto cleanup_recv_data
;
2468 if ((byte_count
% 4096) == 0)
2469 (void) alarm(curclass
.timeout
);
2475 if ((c
= getc(instr
)) != '\n') {
2481 if ((byte_count
% 4096) == 0)
2482 (void) alarm(curclass
.timeout
);
2484 FILESIZECHECK(byteswritten
);
2485 (void) putc ('\r', outstr
);
2486 if (c
== '\0' || c
== EOF
)
2491 FILESIZECHECK(byteswritten
);
2492 (void) putc(c
, outstr
);
2503 "WARNING! %d bare linefeeds received in ASCII mode",
2505 reply(0, "File may not have transferred correctly.");
2508 goto cleanup_recv_data
;
2511 reply(550, "Unimplemented TYPE %d in receive_data", type
);
2512 goto cleanup_recv_data
;
2514 #undef FILESIZECHECK
2518 perror_reply(426, "Data Connection");
2519 goto cleanup_recv_data
;
2523 perror_reply(452, "Error writing file");
2524 goto cleanup_recv_data
;
2528 (void) sigaction(SIGALRM
, &sa_saved
, NULL
);
2543 struct sockinet
*su
= NULL
;
2544 static char hbuf
[NI_MAXHOST
], sbuf
[NI_MAXSERV
];
2545 unsigned char *a
, *p
;
2547 off_t otbi
, otbo
, otb
;
2549 a
= p
= (unsigned char *)NULL
;
2551 reply(-211, "%s FTP server status:", hostname
);
2552 reply(0, "Version: %s", EMPTYSTR(version
) ? "<suppressed>" : version
);
2554 if (!getnameinfo((struct sockaddr
*)&his_addr
.si_su
, his_addr
.su_len
,
2555 hbuf
, sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
)
2556 && strcmp(remotehost
, hbuf
) != 0)
2557 reply(0, "Connected to %s (%s)", remotehost
, hbuf
);
2559 reply(0, "Connected to %s", remotehost
);
2562 if (curclass
.type
== CLASS_GUEST
)
2563 reply(0, "Logged in anonymously");
2565 reply(0, "Logged in as %s%s", pw
->pw_name
,
2566 curclass
.type
== CLASS_CHROOT
? " (chroot)" : "");
2567 } else if (askpasswd
)
2568 reply(0, "Waiting for password");
2570 reply(0, "Waiting for user name");
2571 cprintf(stdout
, " TYPE: %s", typenames
[type
]);
2572 if (type
== TYPE_A
|| type
== TYPE_E
)
2573 cprintf(stdout
, ", FORM: %s", formnames
[form
]);
2574 if (type
== TYPE_L
) {
2576 cprintf(stdout
, " %d", NBBY
);
2578 /* XXX: `bytesize' needs to be defined in this case */
2579 cprintf(stdout
, " %d", bytesize
);
2582 cprintf(stdout
, "; STRUcture: %s; transfer MODE: %s\r\n",
2583 strunames
[stru
], modenames
[mode
]);
2586 reply(0, "Data connection open");
2588 } else if (pdata
!= -1) {
2589 reply(0, "in Passive mode");
2590 if (curclass
.advertise
.su_len
!= 0)
2591 su
= &curclass
.advertise
;
2596 } else if (usedefault
== 0) {
2597 su
= (struct sockinet
*)&data_dest
;
2600 reply(0, "EPSV only mode (EPSV ALL)");
2605 if (su
->su_family
== AF_INET
) {
2606 a
= (unsigned char *) &su
->su_addr
;
2607 p
= (unsigned char *) &su
->su_port
;
2608 #define UC(b) (((int) b) & 0xff)
2609 reply(0, "%s (%d,%d,%d,%d,%d,%d)",
2610 ispassive
? "PASV" : "PORT" ,
2611 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
2612 UC(p
[0]), UC(p
[1]));
2620 switch (su
->su_family
) {
2622 a
= (unsigned char *) &su
->su_addr
;
2623 p
= (unsigned char *) &su
->su_port
;
2624 alen
= sizeof(su
->su_addr
);
2629 a
= (unsigned char *) &su
->su_6addr
;
2630 p
= (unsigned char *) &su
->su_port
;
2631 alen
= sizeof(su
->su_6addr
);
2640 cprintf(stdout
, " %s (%d,%d",
2641 ispassive
? "LPSV" : "LPRT", af
, alen
);
2642 for (i
= 0; i
< alen
; i
++)
2643 cprintf(stdout
, ",%d", UC(a
[i
]));
2644 cprintf(stdout
, ",%d,%d,%d)\r\n",
2645 2, UC(p
[0]), UC(p
[1]));
2652 af
= af2epsvproto(su
->su_family
);
2655 struct sockinet tmp
;
2659 if (tmp
.su_family
== AF_INET6
)
2660 tmp
.su_scope_id
= 0;
2662 if (getnameinfo((struct sockaddr
*)&tmp
.si_su
,
2663 tmp
.su_len
, hbuf
, sizeof(hbuf
), sbuf
, sizeof(sbuf
),
2664 NI_NUMERICHOST
| NI_NUMERICSERV
) == 0)
2665 reply(0, "%s (|%d|%s|%s|)",
2666 ispassive
? "EPSV" : "EPRT",
2670 reply(0, "No data connection");
2674 "Data sent: " LLF
" byte%s in " LLF
" file%s",
2675 (LLT
)total_data_out
, PLURAL(total_data_out
),
2676 (LLT
)total_files_out
, PLURAL(total_files_out
));
2678 "Data received: " LLF
" byte%s in " LLF
" file%s",
2679 (LLT
)total_data_in
, PLURAL(total_data_in
),
2680 (LLT
)total_files_in
, PLURAL(total_files_in
));
2682 "Total data: " LLF
" byte%s in " LLF
" file%s",
2683 (LLT
)total_data
, PLURAL(total_data
),
2684 (LLT
)total_files
, PLURAL(total_files
));
2686 otbi
= total_bytes_in
;
2687 otbo
= total_bytes_out
;
2689 reply(0, "Traffic sent: " LLF
" byte%s in " LLF
" transfer%s",
2690 (LLT
)otbo
, PLURAL(otbo
),
2691 (LLT
)total_xfers_out
, PLURAL(total_xfers_out
));
2692 reply(0, "Traffic received: " LLF
" byte%s in " LLF
" transfer%s",
2693 (LLT
)otbi
, PLURAL(otbi
),
2694 (LLT
)total_xfers_in
, PLURAL(total_xfers_in
));
2695 reply(0, "Total traffic: " LLF
" byte%s in " LLF
" transfer%s",
2696 (LLT
)otb
, PLURAL(otb
),
2697 (LLT
)total_xfers
, PLURAL(total_xfers
));
2699 if (logged_in
&& !CURCLASS_FLAGS_ISSET(private)) {
2703 reply(0, "Class: %s, type: %s",
2704 curclass
.classname
, CURCLASSTYPE
);
2705 reply(0, "Check PORT/LPRT commands: %sabled",
2706 CURCLASS_FLAGS_ISSET(checkportcmd
) ? "en" : "dis");
2707 if (! EMPTYSTR(curclass
.display
))
2708 reply(0, "Display file: %s", curclass
.display
);
2709 if (! EMPTYSTR(curclass
.notify
))
2710 reply(0, "Notify fileglob: %s", curclass
.notify
);
2711 reply(0, "Idle timeout: " LLF
", maximum timeout: " LLF
,
2712 (LLT
)curclass
.timeout
, (LLT
)curclass
.maxtimeout
);
2713 reply(0, "Current connections: %d", connections
);
2714 if (curclass
.limit
== -1)
2715 reply(0, "Maximum connections: unlimited");
2717 reply(0, "Maximum connections: " LLF
,
2718 (LLT
)curclass
.limit
);
2719 if (curclass
.limitfile
)
2720 reply(0, "Connection limit exceeded message file: %s",
2721 conffilename(curclass
.limitfile
));
2722 if (! EMPTYSTR(curclass
.chroot
))
2723 reply(0, "Chroot format: %s", curclass
.chroot
);
2724 reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2725 CURCLASS_FLAGS_ISSET(denyquick
) ? "en" : "dis");
2726 if (! EMPTYSTR(curclass
.homedir
))
2727 reply(0, "Homedir format: %s", curclass
.homedir
);
2728 if (curclass
.maxfilesize
== -1)
2729 reply(0, "Maximum file size: unlimited");
2731 reply(0, "Maximum file size: " LLF
,
2732 (LLT
)curclass
.maxfilesize
);
2733 if (! EMPTYSTR(curclass
.motd
))
2734 reply(0, "MotD file: %s", conffilename(curclass
.motd
));
2736 "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2737 CURCLASS_FLAGS_ISSET(modify
) ? "en" : "dis");
2738 reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2739 CURCLASS_FLAGS_ISSET(upload
) ? "en" : "dis");
2740 reply(0, "Sanitize file names: %sabled",
2741 CURCLASS_FLAGS_ISSET(sanenames
) ? "en" : "dis");
2742 reply(0, "PASV/LPSV/EPSV connections: %sabled",
2743 CURCLASS_FLAGS_ISSET(passive
) ? "en" : "dis");
2744 if (curclass
.advertise
.su_len
!= 0) {
2745 char buf
[50]; /* big enough for IPv6 address */
2748 bp
= inet_ntop(curclass
.advertise
.su_family
,
2749 (void *)&curclass
.advertise
.su_addr
,
2752 reply(0, "PASV advertise address: %s", bp
);
2754 if (curclass
.portmin
&& curclass
.portmax
)
2755 reply(0, "PASV port range: " LLF
" - " LLF
,
2756 (LLT
)curclass
.portmin
, (LLT
)curclass
.portmax
);
2757 if (curclass
.rateget
)
2758 reply(0, "Rate get limit: " LLF
" bytes/sec",
2759 (LLT
)curclass
.rateget
);
2761 reply(0, "Rate get limit: disabled");
2762 if (curclass
.rateput
)
2763 reply(0, "Rate put limit: " LLF
" bytes/sec",
2764 (LLT
)curclass
.rateput
);
2766 reply(0, "Rate put limit: disabled");
2767 if (curclass
.mmapsize
)
2768 reply(0, "Mmap size: " LLF
, (LLT
)curclass
.mmapsize
);
2770 reply(0, "Mmap size: disabled");
2771 if (curclass
.readsize
)
2772 reply(0, "Read size: " LLF
, (LLT
)curclass
.readsize
);
2774 reply(0, "Read size: default");
2775 if (curclass
.writesize
)
2776 reply(0, "Write size: " LLF
, (LLT
)curclass
.writesize
);
2778 reply(0, "Write size: default");
2779 if (curclass
.recvbufsize
)
2780 reply(0, "Receive buffer size: " LLF
,
2781 (LLT
)curclass
.recvbufsize
);
2783 reply(0, "Receive buffer size: default");
2784 if (curclass
.sendbufsize
)
2785 reply(0, "Send buffer size: " LLF
,
2786 (LLT
)curclass
.sendbufsize
);
2788 reply(0, "Send buffer size: default");
2789 if (curclass
.sendlowat
)
2790 reply(0, "Send low water mark: " LLF
,
2791 (LLT
)curclass
.sendlowat
);
2793 reply(0, "Send low water mark: default");
2794 reply(0, "Umask: %.04o", curclass
.umask
);
2795 for (cp
= curclass
.conversions
; cp
!= NULL
; cp
=cp
->next
) {
2796 if (cp
->suffix
== NULL
|| cp
->types
== NULL
||
2797 cp
->command
== NULL
)
2799 reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2800 cp
->suffix
, cp
->types
, cp
->disable
, cp
->command
);
2804 reply(211, "End of status");
2808 fatal(const char *s
)
2811 reply(451, "Error in server: %s\n", s
);
2812 reply(221, "Closing connection due to server error.");
2819 * depending on the value of n, display fmt with a trailing CRLF and
2821 * n < -1 prefix the message with abs(n) + "-" (initial line)
2822 * n == 0 prefix the message with 4 spaces (middle lines)
2823 * n > 0 prefix the message with n + " " (final line)
2826 reply(int n
, const char *fmt
, ...)
2828 char msg
[MAXPATHLEN
* 2 + 100];
2834 b
= snprintf(msg
, sizeof(msg
), " ");
2836 b
= snprintf(msg
, sizeof(msg
), "%d-", -n
);
2838 b
= snprintf(msg
, sizeof(msg
), "%d ", n
);
2840 vsnprintf(msg
+ b
, sizeof(msg
) - b
, fmt
, ap
);
2842 cprintf(stdout
, "%s\r\n", msg
);
2843 (void)fflush(stdout
);
2845 syslog(LOG_DEBUG
, "<--- %s", msg
);
2849 logremotehost(struct sockinet
*who
)
2852 #if defined(HAVE_SOCKADDR_SNPRINTF)
2856 struct sockaddr
*sa
= (struct sockaddr
*)&who
->si_su
;
2857 if (getnameinfo(sa
, who
->su_len
, remotehost
, sizeof(remotehost
), NULL
,
2859 strlcpy(remotehost
, "?", sizeof(remotehost
));
2860 #if defined(HAVE_SOCKADDR_SNPRINTF)
2861 sockaddr_snprintf(abuf
, sizeof(abuf
), "%a", sa
);
2862 snprintf(remoteloghost
, sizeof(remoteloghost
), "%s(%s)", remotehost
,
2865 strlcpy(remoteloghost
, remotehost
, sizeof(remoteloghost
));
2868 #if defined(HAVE_SETPROCTITLE)
2869 snprintf(proctitle
, sizeof(proctitle
), "%s: connected", remotehost
);
2870 setproctitle("%s", proctitle
);
2871 #endif /* defined(HAVE_SETPROCTITLE) */
2873 syslog(LOG_INFO
, "connection from %s to %s",
2874 remoteloghost
, hostname
);
2878 * Record logout in wtmp file and exit with supplied status.
2879 * NOTE: because this is called from signal handlers it cannot
2880 * use stdio (or call other functions that use stdio).
2883 dologout(int status
)
2886 * Prevent reception of SIGURG from resulting in a resumption
2887 * back to the main program loop.
2893 if (!notickets
&& krbtkfile_env
)
2894 unlink(krbtkfile_env
);
2897 /* beware of flushing buffers after a SIGPIPE */
2898 if (xferlogfd
!= -1)
2911 reply(426, "Transfer aborted. Data connection closed.");
2912 reply(226, "Abort successful");
2913 transflag
= 0; /* flag that the transfer has aborted */
2924 if (file_size
!= (off_t
) -1)
2926 "Status: " LLF
" of " LLF
" byte%s transferred",
2927 (LLT
)byte_count
, (LLT
)file_size
,
2928 PLURAL(byte_count
));
2930 reply(213, "Status: " LLF
" byte%s transferred",
2931 (LLT
)byte_count
, PLURAL(byte_count
));
2935 * Call when urgflag != 0 to handle Out Of Band commands.
2936 * Returns non zero if the OOB command aborted the transfer
2937 * by setting transflag to 0. (c.f., "ABOR").
2948 /* only process if transfer occurring */
2952 ret
= get_line(cp
, sizeof(tmpline
)-1, stdin
);
2954 reply(221, "You could at least say goodbye.");
2956 } else if (ret
== -2) {
2957 /* Ignore truncated command */
2958 /* XXX: abort xfer with "500 command too long", & return 1 ? */
2962 * Manually parse OOB commands, because we can't
2963 * recursively call the yacc parser...
2965 if (strcasecmp(cp
, "ABOR\r\n") == 0) {
2967 } else if (strcasecmp(cp
, "STAT\r\n") == 0) {
2970 /* XXX: error with "500 unknown command" ? */
2972 return (transflag
== 0);
2976 bind_pasv_addr(void)
2978 static int passiveport
;
2981 len
= pasv_addr
.su_len
;
2982 if (curclass
.portmin
== 0 && curclass
.portmax
== 0) {
2983 pasv_addr
.su_port
= 0;
2984 return (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
));
2987 if (passiveport
== 0) {
2989 passiveport
= rand() % (curclass
.portmax
- curclass
.portmin
)
2996 if (port
> curclass
.portmax
)
2997 port
= curclass
.portmin
;
2998 else if (port
== passiveport
) {
3002 pasv_addr
.su_port
= htons(port
);
3003 if (bind(pdata
, (struct sockaddr
*)&pasv_addr
.si_su
, len
) == 0)
3005 if (errno
!= EADDRINUSE
)
3013 * Note: a response of 425 is not mentioned as a possible response to
3014 * the PASV command in RFC959. However, it has been blessed as
3015 * a legitimate response by Jon Postel in a telephone conversation
3016 * with Rick Adams on 25 Jan 89.
3027 pdata
= socket(AF_INET
, SOCK_STREAM
, 0);
3028 if (pdata
< 0 || !logged_in
) {
3029 perror_reply(425, "Can't open passive connection");
3032 pasv_addr
= ctrl_addr
;
3034 if (bind_pasv_addr() < 0)
3036 len
= pasv_addr
.su_len
;
3037 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3039 pasv_addr
.su_len
= len
;
3040 if (curclass
.recvbufsize
) {
3041 recvbufsize
= curclass
.recvbufsize
;
3042 if (setsockopt(pdata
, SOL_SOCKET
, SO_RCVBUF
, &recvbufsize
,
3044 syslog(LOG_WARNING
, "setsockopt(SO_RCVBUF, %d): %m",
3047 if (listen(pdata
, 1) < 0)
3049 if (curclass
.advertise
.su_len
!= 0)
3050 a
= (char *) &curclass
.advertise
.su_addr
;
3052 a
= (char *) &pasv_addr
.su_addr
;
3053 p
= (char *) &pasv_addr
.su_port
;
3055 #define UC(b) (((int) b) & 0xff)
3057 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a
[0]),
3058 UC(a
[1]), UC(a
[2]), UC(a
[3]), UC(p
[0]), UC(p
[1]));
3062 (void) close(pdata
);
3064 perror_reply(425, "Can't open passive connection");
3069 * convert protocol identifier to/from AF
3072 lpsvproto2af(int proto
)
3088 af2lpsvproto(int af
)
3104 epsvproto2af(int proto
)
3120 af2epsvproto(int af
)
3136 * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3137 * 229 Entering Extended Passive Mode (|||port|)
3140 long_passive(const char *cmd
, int pf
)
3146 syslog(LOG_NOTICE
, "long passive but not logged in");
3147 reply(503, "Login with USER first.");
3151 if (pf
!= PF_UNSPEC
&& ctrl_addr
.su_family
!= pf
) {
3153 * XXX: only EPRT/EPSV ready clients will understand this
3155 if (strcmp(cmd
, "EPSV") != 0)
3156 reply(501, "Network protocol mismatch"); /*XXX*/
3158 epsv_protounsupp("Network protocol mismatch");
3165 pdata
= socket(ctrl_addr
.su_family
, SOCK_STREAM
, 0);
3167 perror_reply(425, "Can't open passive connection");
3170 pasv_addr
= ctrl_addr
;
3171 if (bind_pasv_addr() < 0)
3173 len
= pasv_addr
.su_len
;
3174 if (getsockname(pdata
, (struct sockaddr
*) &pasv_addr
.si_su
, &len
) < 0)
3176 pasv_addr
.su_len
= len
;
3177 if (listen(pdata
, 1) < 0)
3179 p
= (char *) &pasv_addr
.su_port
;
3181 #define UC(b) (((int) b) & 0xff)
3183 if (strcmp(cmd
, "LPSV") == 0) {
3184 struct sockinet
*advert
;
3186 if (curclass
.advertise
.su_len
!= 0)
3187 advert
= &curclass
.advertise
;
3189 advert
= &pasv_addr
;
3190 switch (advert
->su_family
) {
3192 a
= (char *) &advert
->su_addr
;
3194 "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3195 4, 4, UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3196 2, UC(p
[0]), UC(p
[1]));
3200 a
= (char *) &advert
->su_6addr
;
3202 "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)",
3204 UC(a
[0]), UC(a
[1]), UC(a
[2]), UC(a
[3]),
3205 UC(a
[4]), UC(a
[5]), UC(a
[6]), UC(a
[7]),
3206 UC(a
[8]), UC(a
[9]), UC(a
[10]), UC(a
[11]),
3207 UC(a
[12]), UC(a
[13]), UC(a
[14]), UC(a
[15]),
3208 2, UC(p
[0]), UC(p
[1]));
3213 } else if (strcmp(cmd
, "EPSV") == 0) {
3214 switch (pasv_addr
.su_family
) {
3219 reply(229, "Entering Extended Passive Mode (|||%d|)",
3220 ntohs(pasv_addr
.su_port
));
3224 /* more proper error code? */
3228 (void) close(pdata
);
3230 perror_reply(425, "Can't open passive connection");
3235 extended_port(const char *arg
)
3241 struct addrinfo hints
;
3242 struct addrinfo
*res
= NULL
;
3244 unsigned long proto
;
3246 tmp
= ftpd_strdup(arg
);
3250 memset(result
, 0, sizeof(result
));
3251 for (i
= 0; i
< 3; i
++) {
3252 q
= strchr(p
, delim
);
3253 if (!q
|| *q
!= delim
)
3260 /* some more sanity checks */
3263 (void)strtoul(result
[2], &p
, 10);
3264 if (errno
|| !*result
[2] || *p
)
3268 proto
= strtoul(result
[0], &p
, 10);
3269 if (errno
|| !*result
[0] || *p
)
3272 memset(&hints
, 0, sizeof(hints
));
3273 hints
.ai_family
= epsvproto2af((int)proto
);
3274 if (hints
.ai_family
< 0)
3276 hints
.ai_socktype
= SOCK_STREAM
;
3277 hints
.ai_flags
= AI_NUMERICHOST
;
3278 if (getaddrinfo(result
[1], result
[2], &hints
, &res
))
3282 if (sizeof(data_dest
) < res
->ai_addrlen
)
3284 memcpy(&data_dest
.si_su
, res
->ai_addr
, res
->ai_addrlen
);
3285 data_dest
.su_len
= res
->ai_addrlen
;
3287 if (his_addr
.su_family
== AF_INET6
&&
3288 data_dest
.su_family
== AF_INET6
) {
3289 /* XXX: more sanity checks! */
3290 data_dest
.su_scope_id
= his_addr
.su_scope_id
;
3301 reply(500, "Invalid argument, rejected.");
3310 epsv_protounsupp("Protocol not supported");
3318 * 522 Protocol not supported (proto,...)
3319 * as we assume address family for control and data connections are the same,
3320 * we do not return the list of address families we support - instead, we
3321 * return the address family of the control connection.
3324 epsv_protounsupp(const char *message
)
3328 proto
= af2epsvproto(ctrl_addr
.su_family
);
3330 reply(501, "%s", message
); /* XXX */
3332 reply(522, "%s, use (%d)", message
, proto
);
3336 * Generate unique name for file with basename "local".
3337 * The file named "local" is already known to exist.
3338 * Generates failure reply on error.
3340 * XXX: this function should under go changes similar to
3341 * the mktemp(3)/mkstemp(3) changes.
3344 gunique(const char *local
)
3346 static char new[MAXPATHLEN
];
3351 cp
= strrchr(local
, '/');
3354 if (stat(cp
? local
: ".", &st
) < 0) {
3355 perror_reply(553, cp
? local
: ".");
3360 for (count
= 1; count
< 100; count
++) {
3361 (void)snprintf(new, sizeof(new) - 1, "%s.%d", local
, count
);
3362 if (stat(new, &st
) < 0)
3365 reply(452, "Unique file name cannot be created.");
3370 * Format and send reply containing system error number.
3373 perror_reply(int code
, const char *string
)
3378 reply(code
, "%s: %s.", string
, strerror(errno
));
3382 static char *onefile
[] = {
3388 send_file_list(const char *whichf
)
3393 FILE *volatile dout
;
3394 char **volatile dirlist
;
3397 int volatile simple
;
3398 int volatile freeglob
;
3409 if (strpbrk(whichf
, "~{[*?") != NULL
) {
3410 int flags
= GLOB_BRACE
|GLOB_NOCHECK
|GLOB_TILDE
|GLOB_LIMIT
;
3412 memset(&gl
, 0, sizeof(gl
));
3414 if (glob(whichf
, flags
, 0, &gl
)) {
3415 reply(450, "Not found");
3416 goto cleanup_send_file_list
;
3417 } else if (gl
.gl_pathc
== 0) {
3419 perror_reply(450, whichf
);
3420 goto cleanup_send_file_list
;
3422 dirlist
= gl
.gl_pathv
;
3424 notglob
= ftpd_strdup(whichf
);
3425 onefile
[0] = notglob
;
3429 /* XXX: } for vi sm */
3431 while ((dirname
= *dirlist
++) != NULL
) {
3432 int trailingslash
= 0;
3434 if (stat(dirname
, &st
) < 0) {
3436 * If user typed "ls -l", etc, and the client
3437 * used NLST, do what the user meant.
3439 /* XXX: nuke this support? */
3440 if (dirname
[0] == '-' && *dirlist
== NULL
&&
3442 const char *argv
[] = { INTERNAL_LS
, "", NULL
};
3445 retrieve(argv
, dirname
);
3446 goto cleanup_send_file_list
;
3448 perror_reply(450, whichf
);
3449 goto cleanup_send_file_list
;
3452 if (S_ISREG(st
.st_mode
)) {
3455 * should we follow RFC959 and not work
3456 * for non directories?
3459 dout
= dataconn("file list", (off_t
)-1, "w");
3461 goto cleanup_send_file_list
;
3464 cprintf(dout
, "%s%s\n", dirname
,
3465 type
== TYPE_A
? "\r" : "");
3467 } else if (!S_ISDIR(st
.st_mode
))
3470 if (dirname
[strlen(dirname
) - 1] == '/')
3473 if ((dirp
= opendir(dirname
)) == NULL
)
3476 while ((dir
= readdir(dirp
)) != NULL
) {
3477 char nbuf
[MAXPATHLEN
];
3479 if (urgflag
&& handleoobcmd())
3480 goto cleanup_send_file_list
;
3482 if (ISDOTDIR(dir
->d_name
) || ISDOTDOTDIR(dir
->d_name
))
3485 (void)snprintf(nbuf
, sizeof(nbuf
), "%s%s%s", dirname
,
3486 trailingslash
? "" : "/", dir
->d_name
);
3489 * We have to do a stat to ensure it's
3490 * not a directory or special file.
3494 * should we follow RFC959 and filter out
3495 * non files ? lukem - NO!, or not until
3496 * our ftp client uses MLS{T,D} for completion.
3498 if (simple
|| (stat(nbuf
, &st
) == 0 &&
3499 S_ISREG(st
.st_mode
))) {
3501 dout
= dataconn("file list", (off_t
)-1,
3504 goto cleanup_send_file_list
;
3508 if (nbuf
[0] == '.' && nbuf
[1] == '/')
3510 cprintf(dout
, "%s%s\n", p
,
3511 type
== TYPE_A
? "\r" : "");
3514 (void) closedir(dirp
);
3518 reply(450, "No files found.");
3519 else if (ferror(dout
) != 0)
3520 perror_reply(451, "Data connection");
3522 reply(226, "Transfer complete.");
3524 cleanup_send_file_list
:
3525 closedataconn(dout
);
3537 conffilename(const char *s
)
3539 static char filename
[MAXPATHLEN
];
3542 strlcpy(filename
, s
, sizeof(filename
));
3544 (void)snprintf(filename
, sizeof(filename
), "%s/%s", confdir
,s
);
3550 * if logging > 1, then based on the arguments, syslog a message:
3551 * if bytes != -1 "<command> <file1> = <bytes> bytes"
3552 * else if file2 != NULL "<command> <file1> <file2>"
3553 * else "<command> <file1>"
3554 * if elapsed != NULL, append "in xxx.yyy seconds"
3555 * if error != NULL, append ": " + error
3557 * if doxferlog != 0, bytes != -1, and command is "get", "put",
3558 * or "append", syslog and/or write a wu-ftpd style xferlog entry
3561 logxfer(const char *command
, off_t bytes
, const char *file1
, const char *file2
,
3562 const struct timeval
*elapsed
, const char *error
)
3564 char buf
[MAXPATHLEN
* 2 + 100];
3565 char realfile1
[MAXPATHLEN
], realfile2
[MAXPATHLEN
];
3566 const char *r1
, *r2
;
3571 if (logging
<=1 && !doxferlog
)
3575 if ((r1
= realpath(file1
, realfile1
)) == NULL
)
3578 if ((r2
= realpath(file2
, realfile2
)) == NULL
)
3585 len
= snprintf(buf
, sizeof(buf
), "%s %s", command
, r1
);
3586 if (bytes
!= (off_t
)-1)
3587 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3588 " = " LLF
" byte%s", (LLT
) bytes
, PLURAL(bytes
));
3589 else if (r2
!= NULL
)
3590 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3592 if (elapsed
!= NULL
)
3593 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3594 " in " LLF
".%.03ld seconds",
3595 (LLT
)elapsed
->tv_sec
,
3596 (long)(elapsed
->tv_usec
/ 1000));
3598 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3600 syslog(LOG_INFO
, "%s", buf
);
3604 * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3606 if (!doxferlog
|| bytes
== -1)
3609 if (strcmp(command
, "get") == 0)
3611 else if (strcmp(command
, "put") == 0 || strcmp(command
, "append") == 0)
3617 len
= snprintf(buf
, sizeof(buf
),
3618 "%.24s " LLF
" %s " LLF
" %s %c %s %c %c %s FTP 0 * %c\n",
3621 * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
3622 * the full date. This may be problematic for accurate log parsing,
3623 * given that syslog messages don't contain the full date.
3627 (elapsed
== NULL
? 0 : elapsed
->tv_sec
+ (elapsed
->tv_usec
> 0)),
3631 type
== TYPE_A
? 'a' : 'b',
3632 "_", /* XXX: take conversions into account? */
3635 curclass
.type
== CLASS_GUEST
? 'a' :
3636 curclass
.type
== CLASS_CHROOT
? 'g' :
3637 curclass
.type
== CLASS_REAL
? 'r' : '?',
3639 curclass
.type
== CLASS_GUEST
? pw
->pw_passwd
: pw
->pw_name
,
3640 error
!= NULL
? 'i' : 'c'
3643 if ((doxferlog
& 2) && xferlogfd
!= -1)
3644 write(xferlogfd
, buf
, len
);
3645 if ((doxferlog
& 1)) {
3646 buf
[len
-1] = '\n'; /* strip \n from syslog message */
3647 syslog(LOG_INFO
, "xferlog: %s", buf
);
3652 * Log the resource usage.
3654 * XXX: more resource usage to logging?
3657 logrusage(const struct rusage
*rusage_before
,
3658 const struct rusage
*rusage_after
)
3660 struct timeval usrtime
, systime
;
3665 timersub(&rusage_after
->ru_utime
, &rusage_before
->ru_utime
, &usrtime
);
3666 timersub(&rusage_after
->ru_stime
, &rusage_before
->ru_stime
, &systime
);
3667 syslog(LOG_INFO
, LLF
".%.03ldu " LLF
".%.03lds %ld+%ldio %ldpf+%ldw",
3668 (LLT
)usrtime
.tv_sec
, (long)(usrtime
.tv_usec
/ 1000),
3669 (LLT
)systime
.tv_sec
, (long)(systime
.tv_usec
/ 1000),
3670 rusage_after
->ru_inblock
- rusage_before
->ru_inblock
,
3671 rusage_after
->ru_oublock
- rusage_before
->ru_oublock
,
3672 rusage_after
->ru_majflt
- rusage_before
->ru_majflt
,
3673 rusage_after
->ru_nswap
- rusage_before
->ru_nswap
);
3677 * Determine if `password' is valid for user given in `pw'.
3678 * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
3681 checkpassword(const struct passwd
*pwent
, const char *password
)
3685 time_t change
, expire
, now
;
3687 change
= expire
= 0;
3692 orig
= pwent
->pw_passwd
; /* save existing password */
3693 expire
= pwent
->pw_expire
;
3694 change
= pwent
->pw_change
;
3695 if (change
== _PASSWORD_CHGNOW
)
3698 if (orig
[0] == '\0') /* don't allow empty passwords */
3701 new = crypt(password
, orig
); /* encrypt given password */
3702 if (strcmp(new, orig
) != 0) /* compare */
3705 if ((expire
&& now
>= expire
) || (change
&& now
>= change
))
3706 return 2; /* check if expired */
3712 ftpd_strdup(const char *s
)
3714 char *new = strdup(s
);
3717 fatal("Local resource failure: malloc");
3723 * As per fprintf(), but increment total_bytes and total_bytes_out,
3724 * by the appropriate amount.
3727 cprintf(FILE *fd
, const char *fmt
, ...)
3733 b
= vfprintf(fd
, fmt
, ap
);
3736 total_bytes_out
+= b
;
3741 * the following code is stolen from imap-uw PAM authentication module and
3745 const char *uname
; /* user name */
3746 int triedonce
; /* if non-zero, tried before */
3750 auth_conv(int num_msg
, const struct pam_message
**msg
,
3751 struct pam_response
**resp
, void *appdata
)
3755 ftpd_cred_t
*cred
= (ftpd_cred_t
*) appdata
;
3756 struct pam_response
*myreply
;
3757 char pbuf
[FTP_BUFLEN
];
3759 if (num_msg
<= 0 || num_msg
> PAM_MAX_NUM_MSG
)
3760 return (PAM_CONV_ERR
);
3761 myreply
= calloc(num_msg
, sizeof *myreply
);
3762 if (myreply
== NULL
)
3765 for (i
= 0; i
< num_msg
; i
++) {
3766 myreply
[i
].resp_retcode
= 0;
3767 myreply
[i
].resp
= NULL
;
3768 switch (msg
[i
]->msg_style
) {
3769 case PAM_PROMPT_ECHO_ON
: /* user */
3770 myreply
[i
].resp
= ftpd_strdup(cred
->uname
);
3771 /* PAM frees resp. */
3773 case PAM_PROMPT_ECHO_OFF
: /* authtok (password) */
3775 * Only send a single 331 reply and
3776 * then expect a PASS.
3778 if (cred
->triedonce
) {
3780 "auth_conv: already performed PAM_PROMPT_ECHO_OFF");
3784 if (msg
[i
]->msg
[0] == '\0') {
3785 (void)strlcpy(pbuf
, "password", sizeof(pbuf
));
3787 /* Uncapitalize msg */
3788 (void)strlcpy(pbuf
, msg
[i
]->msg
, sizeof(pbuf
));
3789 if (isupper((unsigned char)pbuf
[0]))
3791 (unsigned char)pbuf
[0]);
3792 /* Remove trailing ':' and whitespace */
3795 if (isspace((unsigned char)pbuf
[n
]) ||
3802 /* Send reply, wait for a response. */
3803 reply(331, "User %s accepted, provide %s.",
3805 (void) alarm(curclass
.timeout
);
3806 ret
= get_line(pbuf
, sizeof(pbuf
)-1, stdin
);
3809 reply(221, "You could at least say goodbye.");
3811 } else if (ret
== -2) {
3812 /* XXX: should we do this reply(-530, ..) ? */
3813 reply(-530, "Command too long.");
3816 /* Ensure it is PASS */
3817 if (strncasecmp(pbuf
, "PASS ", 5) != 0) {
3819 "auth_conv: unexpected reply '%.4s'", pbuf
);
3820 /* XXX: should we do this reply(-530, ..) ? */
3821 reply(-530, "Unexpected reply '%.4s'.", pbuf
);
3824 /* Strip CRLF from "PASS" reply */
3827 (pbuf
[n
] == '\r' || pbuf
[n
] == '\n'))
3829 /* Copy password into reply */
3830 myreply
[i
].resp
= ftpd_strdup(pbuf
+5);
3831 /* PAM frees resp. */
3836 default: /* unknown message style */
3847 return PAM_CONV_ERR
;
3851 * Attempt to authenticate the user using PAM. Returns 0 if the user is
3852 * authenticated, or 1 if not authenticated. If some sort of PAM system
3853 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3854 * function returns -1. This can be used as an indication that we should
3855 * fall back to a different authentication mechanism.
3856 * pw maybe be updated to a new user if PAM_USER changes from curname.
3861 const char *tmpl_user
;
3865 ftpd_cred_t auth_cred
= { curname
, 0 };
3866 struct pam_conv conv
= { &auth_conv
, &auth_cred
};
3868 e
= pam_start("ftpd", curname
, &conv
, &pamh
);
3869 if (e
!= PAM_SUCCESS
) {
3871 * In OpenPAM, it's OK to pass NULL to pam_strerror()
3872 * if context creation has failed in the first place.
3874 syslog(LOG_ERR
, "pam_start: %s", pam_strerror(NULL
, e
));
3878 e
= pam_set_item(pamh
, PAM_RHOST
, remotehost
);
3879 if (e
!= PAM_SUCCESS
) {
3880 syslog(LOG_ERR
, "pam_set_item(PAM_RHOST): %s",
3881 pam_strerror(pamh
, e
));
3882 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3883 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3889 e
= pam_set_item(pamh
, PAM_SOCKADDR
, &his_addr
);
3890 if (e
!= PAM_SUCCESS
) {
3891 syslog(LOG_ERR
, "pam_set_item(PAM_SOCKADDR): %s",
3892 pam_strerror(pamh
, e
));
3893 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3894 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3900 e
= pam_authenticate(pamh
, 0);
3902 syslog(LOG_DEBUG
, "pam_authenticate: user '%s' returned %d",
3907 * With PAM we support the concept of a "template"
3908 * user. The user enters a login name which is
3909 * authenticated by PAM, usually via a remote service
3910 * such as RADIUS or TACACS+. If authentication
3911 * succeeds, a different but related "template" name
3912 * is used for setting the credentials, shell, and
3913 * home directory. The name the user enters need only
3914 * exist on the remote authentication server, but the
3915 * template name must be present in the local password
3918 * This is supported by two various mechanisms in the
3919 * individual modules. However, from the application's
3920 * point of view, the template user is always passed
3921 * back as a changed value of the PAM_USER item.
3923 if ((e
= pam_get_item(pamh
, PAM_USER
, &item
)) ==
3925 tmpl_user
= (const char *) item
;
3927 || strcmp(pw
->pw_name
, tmpl_user
) != 0) {
3928 pw
= sgetpwnam(tmpl_user
);
3931 "auth_pam: PAM changed "
3932 "user from '%s' to '%s'",
3933 curname
, pw
->pw_name
);
3934 (void)strlcpy(curname
, pw
->pw_name
,
3938 syslog(LOG_ERR
, "Couldn't get PAM_USER: %s",
3939 pam_strerror(pamh
, e
));
3944 case PAM_USER_UNKNOWN
:
3950 syslog(LOG_ERR
, "pam_authenticate: %s", pam_strerror(pamh
, e
));
3956 e
= pam_acct_mgmt(pamh
, 0);
3957 if (e
!= PAM_SUCCESS
) {
3958 syslog(LOG_ERR
, "pam_acct_mgmt: %s",
3959 pam_strerror(pamh
, e
));
3965 if ((e
= pam_end(pamh
, e
)) != PAM_SUCCESS
) {
3966 syslog(LOG_ERR
, "pam_end: %s", pam_strerror(pamh
, e
));
3973 #endif /* USE_PAM */