Annotation of src/libexec/ftpd/ftpd.c, Revision 1.198
1.198 ! dholland 1: /* $NetBSD: ftpd.c,v 1.197 2011/09/16 16:13:17 plunky Exp $ */
1.67 itojun 2:
3: /*
1.192 lukem 4: * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
1.67 itojun 5: * All rights reserved.
1.73 lukem 6: *
7: * This code is derived from software contributed to The NetBSD Foundation
8: * by Luke Mewburn.
9: *
1.67 itojun 10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
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.
1.73 lukem 18: *
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.
1.67 itojun 30: */
1.13 cgd 31:
1.1 cgd 32: /*
1.8 deraadt 33: * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
34: * The Regents of the University of California. All rights reserved.
1.1 cgd 35: *
36: * Redistribution and use in source and binary forms, with or without
37: * modification, are permitted provided that the following conditions
38: * are met:
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.
1.156 agc 44: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 45: * may be used to endorse or promote products derived from this software
46: * without specific prior written permission.
47: *
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
58: * SUCH DAMAGE.
59: */
60:
1.62 lukem 61: /*
1.73 lukem 62: * Copyright (C) 1997 and 1998 WIDE Project.
1.62 lukem 63: * All rights reserved.
1.73 lukem 64: *
1.62 lukem 65: * Redistribution and use in source and binary forms, with or without
66: * modification, are permitted provided that the following conditions
67: * are met:
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.
1.73 lukem 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.
76: *
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
87: * SUCH DAMAGE.
1.62 lukem 88: */
89:
1.25 christos 90: #include <sys/cdefs.h>
1.1 cgd 91: #ifndef lint
1.186 lukem 92: __COPYRIGHT("@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\
93: The Regents of the University of California. All rights reserved.");
1.1 cgd 94: #endif /* not lint */
95:
96: #ifndef lint
1.13 cgd 97: #if 0
1.17 cjs 98: static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
1.13 cgd 99: #else
1.198 ! dholland 100: __RCSID("$NetBSD: ftpd.c,v 1.197 2011/09/16 16:13:17 plunky Exp $");
1.13 cgd 101: #endif
1.1 cgd 102: #endif /* not lint */
103:
104: /*
105: * FTP server.
106: */
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>
1.139 enami 112: #include <sys/mman.h>
113: #include <sys/resource.h>
1.1 cgd 114:
115: #include <netinet/in.h>
116: #include <netinet/in_systm.h>
117: #include <netinet/ip.h>
118:
119: #define FTP_NAMES
120: #include <arpa/ftp.h>
121: #include <arpa/inet.h>
122: #include <arpa/telnet.h>
123:
1.8 deraadt 124: #include <ctype.h>
1.1 cgd 125: #include <dirent.h>
1.8 deraadt 126: #include <err.h>
127: #include <errno.h>
1.1 cgd 128: #include <fcntl.h>
1.21 cjs 129: #include <fnmatch.h>
1.8 deraadt 130: #include <glob.h>
1.81 lukem 131: #include <grp.h>
1.8 deraadt 132: #include <limits.h>
133: #include <netdb.h>
1.1 cgd 134: #include <pwd.h>
1.167 peter 135: #include <poll.h>
1.8 deraadt 136: #include <signal.h>
1.93 lukem 137: #include <stdarg.h>
1.1 cgd 138: #include <stdio.h>
139: #include <stdlib.h>
140: #include <string.h>
1.8 deraadt 141: #include <syslog.h>
142: #include <time.h>
1.118 lukem 143: #include <tzfile.h>
1.8 deraadt 144: #include <unistd.h>
1.73 lukem 145: #include <util.h>
1.141 christos 146: #ifdef SUPPORT_UTMP
1.78 lukem 147: #include <utmp.h>
1.141 christos 148: #endif
149: #ifdef SUPPORT_UTMPX
150: #include <utmpx.h>
151: #endif
1.25 christos 152: #ifdef SKEY
153: #include <skey.h>
154: #endif
1.38 mycroft 155: #ifdef KERBEROS5
1.86 aidan 156: #include <com_err.h>
1.69 christos 157: #include <krb5/krb5.h>
1.38 mycroft 158: #endif
1.8 deraadt 159:
1.164 christos 160: #ifdef LOGIN_CAP
161: #include <login_cap.h>
162: #endif
163:
164: #ifdef USE_PAM
165: #include <security/pam_appl.h>
166: #endif
167:
1.84 lukem 168: #define GLOBAL
1.24 lukem 169: #include "extern.h"
1.1 cgd 170: #include "pathnames.h"
1.78 lukem 171: #include "version.h"
1.1 cgd 172:
1.178 christos 173: static sig_atomic_t transflag;
174: static sig_atomic_t urgflag;
1.158 lukem 175:
1.1 cgd 176: int data;
1.167 peter 177: int Dflag;
1.51 msaitoh 178: int sflag;
1.1 cgd 179: int stru; /* avoid C keyword */
180: int mode;
1.111 lukem 181: int dataport; /* use specific data port */
182: int dopidfile; /* maintain pid file */
1.101 lukem 183: int doutmp; /* update utmp file */
1.102 lukem 184: int dowtmp; /* update wtmp file */
1.154 lukem 185: int doxferlog; /* syslog/write wu-ftpd style xferlog entries */
186: int xferlogfd; /* fd to write wu-ftpd xferlog entries to */
1.180 christos 187: int getnameopts; /* flags for use with getname() */
1.101 lukem 188: int dropprivs; /* if privileges should or have been dropped */
189: int mapped; /* IPv4 connection on AF_INET6 socket */
1.1 cgd 190: off_t file_size;
191: off_t byte_count;
1.13 cgd 192: static char ttyline[20];
1.164 christos 193:
194: #ifdef USE_PAM
1.183 lukem 195: static int auth_pam(void);
1.164 christos 196: pam_handle_t *pamh = NULL;
197: #endif
198:
1.141 christos 199: #ifdef SUPPORT_UTMP
1.78 lukem 200: static struct utmp utmp; /* for utmp */
1.141 christos 201: #endif
202: #ifdef SUPPORT_UTMPX
203: static struct utmpx utmpx; /* for utmpx */
204: #endif
1.57 lukem 205:
1.111 lukem 206: static const char *anondir = NULL;
1.117 lukem 207: static const char *confdir = _DEFAULT_CONFDIR;
1.24 lukem 208:
1.157 lukem 209: static char *curname; /* current USER name */
210: static size_t curname_len; /* length of curname (include NUL) */
211:
1.38 mycroft 212: #if defined(KERBEROS) || defined(KERBEROS5)
1.91 fredb 213: int has_ccache = 0;
1.13 cgd 214: int notickets = 1;
215: char *krbtkfile_env = NULL;
1.91 fredb 216: char *tty = ttyline;
1.92 explorer 217: int login_krb5_forwardable_tgt = 0;
1.73 lukem 218: #endif
1.4 cgd 219:
1.67 itojun 220: int epsvall = 0;
221:
1.1 cgd 222: /*
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...
226: */
227: #define SWAITMAX 90 /* wait at most 90 seconds */
228: #define SWAITINT 5 /* interval between retries */
229:
230: int swaitmax = SWAITMAX;
231: int swaitint = SWAITINT;
232:
1.139 enami 233: enum send_status {
234: SS_SUCCESS,
1.158 lukem 235: SS_ABORTED, /* transfer aborted */
1.139 enami 236: SS_NO_TRANSFER, /* no transfer made yet */
237: SS_FILE_ERROR, /* file read error */
238: SS_DATA_ERROR /* data send error */
239: };
240:
1.88 lukem 241: static int bind_pasv_addr(void);
242: static int checkuser(const char *, const char *, int, int, char **);
243: static int checkaccess(const char *);
1.116 lukem 244: static int checkpassword(const struct passwd *, const char *);
1.183 lukem 245: static void do_pass(int, int, const char *);
1.88 lukem 246: static void end_login(void);
247: static FILE *getdatasock(const char *);
248: static char *gunique(const char *);
1.160 christos 249: static void login_utmp(const char *, const char *, const char *,
250: struct sockinet *);
1.118 lukem 251: static void logremotehost(struct sockinet *);
1.195 joerg 252: __dead static void lostconn(int);
253: __dead static void toolong(int);
254: __dead static void sigquit(int);
1.158 lukem 255: static void sigurg(int);
256: static int handleoobcmd(void);
1.88 lukem 257: static int receive_data(FILE *, FILE *);
1.139 enami 258: static int send_data(FILE *, FILE *, const struct stat *, int);
1.88 lukem 259: static struct passwd *sgetpwnam(const char *);
1.139 enami 260: static int write_data(int, char *, size_t, off_t *, struct timeval *,
261: int);
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 *);
1.141 christos 267: static void logout_utmp(void);
1.8 deraadt 268:
1.88 lukem 269: int main(int, char *[]);
1.26 mellon 270:
1.92 explorer 271: #if defined(KERBEROS)
1.88 lukem 272: int klogin(struct passwd *, char *, char *, char *);
273: void kdestroy(void);
1.26 mellon 274: #endif
1.92 explorer 275: #if defined(KERBEROS5)
276: int k5login(struct passwd *, char *, char *, char *);
277: void k5destroy(void);
278: #endif
1.63 lukem 279:
1.8 deraadt 280: int
1.88 lukem 281: main(int argc, char *argv[])
1.1 cgd 282: {
1.176 mrg 283: int ch, on = 1, tos, keepalive;
284: socklen_t addrlen;
1.73 lukem 285: #ifdef KERBEROS5
1.76 lukem 286: krb5_error_code kerror;
1.38 mycroft 287: #endif
1.111 lukem 288: char *p;
1.154 lukem 289: const char *xferlogname = NULL;
1.146 itojun 290: long l;
1.158 lukem 291: struct sigaction sa;
1.167 peter 292: sa_family_t af = AF_UNSPEC;
1.1 cgd 293:
1.87 lukem 294: connections = 1;
1.171 christos 295: ftpd_debug = 0;
1.35 lukem 296: logging = 0;
1.87 lukem 297: pdata = -1;
1.167 peter 298: Dflag = 0;
1.51 msaitoh 299: sflag = 0;
1.111 lukem 300: dataport = 0;
301: dopidfile = 1; /* default: DO use a pid file to count users */
1.118 lukem 302: doutmp = 0; /* default: Do NOT log to utmp */
1.102 lukem 303: dowtmp = 1; /* default: DO log to wtmp */
1.118 lukem 304: doxferlog = 0; /* default: Do NOT syslog xferlog */
1.154 lukem 305: xferlogfd = -1; /* default: Do NOT write xferlog file */
1.180 christos 306: getnameopts = 0; /* default: xlate addrs to name */
1.101 lukem 307: dropprivs = 0;
308: mapped = 0;
1.87 lukem 309: usedefault = 1;
1.111 lukem 310: emailaddr = NULL;
1.80 lukem 311: hostname[0] = '\0';
1.100 lukem 312: homedir[0] = '\0';
1.95 lukem 313: gidcount = 0;
1.123 aidan 314: is_oob = 0;
1.111 lukem 315: version = FTPD_VERSION;
316:
317: /*
318: * LOG_NDELAY sets up the logging connection immediately,
319: * necessary for anonymous ftp's that chroot and can't do it later.
320: */
321: openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
1.1 cgd 322:
1.167 peter 323: while ((ch = getopt(argc, argv,
1.180 christos 324: "46a:c:C:Dde:h:HlL:nP:qQrst:T:uUvV:wWX")) != -1) {
1.8 deraadt 325: switch (ch) {
1.167 peter 326: case '4':
327: af = AF_INET;
328: break;
329:
330: case '6':
331: af = AF_INET6;
332: break;
333:
1.22 cjs 334: case 'a':
335: anondir = optarg;
336: break;
337:
1.34 lukem 338: case 'c':
1.111 lukem 339: confdir = optarg;
1.34 lukem 340: break;
1.73 lukem 341:
1.35 lukem 342: case 'C':
1.188 lukem 343: if ((p = strchr(optarg, '@')) != NULL) {
344: *p++ = '\0';
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;
349: his_addr.su_len =
350: sizeof(his_addr.si_su.su_sin);
351: #ifdef INET6
352: } else if (inet_pton(AF_INET6, p,
353: &his_addr.su_6addr) == 1) {
354: his_addr.su_family = AF_INET6;
355: his_addr.su_len =
356: sizeof(his_addr.si_su.su_sin6);
357: #endif
358: } else
359: his_addr.su_family = AF_UNSPEC;
360: }
1.81 lukem 361: pw = sgetpwnam(optarg);
1.73 lukem 362: exit(checkaccess(optarg) ? 0 : 1);
1.35 lukem 363: /* NOTREACHED */
1.34 lukem 364:
1.167 peter 365: case 'D':
366: Dflag = 1;
367: break;
368:
1.1 cgd 369: case 'd':
1.22 cjs 370: case 'v': /* deprecated */
1.171 christos 371: ftpd_debug = 1;
1.1 cgd 372: break;
373:
1.111 lukem 374: case 'e':
375: emailaddr = optarg;
376: break;
377:
1.80 lukem 378: case 'h':
379: strlcpy(hostname, optarg, sizeof(hostname));
1.99 lukem 380: break;
381:
382: case 'H':
383: if (gethostname(hostname, sizeof(hostname)) == -1)
384: hostname[0] = '\0';
385: hostname[sizeof(hostname) - 1] = '\0';
1.80 lukem 386: break;
387:
1.1 cgd 388: case 'l':
1.8 deraadt 389: logging++; /* > 1 == extra logging */
1.1 cgd 390: break;
391:
1.154 lukem 392: case 'L':
393: xferlogname = optarg;
394: break;
395:
1.180 christos 396: case 'n':
397: getnameopts = NI_NUMERICHOST;
398: break;
399:
1.111 lukem 400: case 'P':
1.146 itojun 401: errno = 0;
402: p = NULL;
403: l = strtol(optarg, &p, 10);
404: if (errno || *optarg == '\0' || *p != '\0' ||
405: l < IPPORT_RESERVED ||
406: l > IPPORT_ANONMAX) {
1.111 lukem 407: syslog(LOG_WARNING, "Invalid dataport %s",
408: optarg);
409: dataport = 0;
410: }
1.146 itojun 411: dataport = (int)l;
1.111 lukem 412: break;
413:
414: case 'q':
415: dopidfile = 1;
416: break;
417:
418: case 'Q':
419: dopidfile = 0;
420: break;
421:
1.101 lukem 422: case 'r':
423: dropprivs = 1;
424: break;
425:
1.51 msaitoh 426: case 's':
427: sflag = 1;
428: break;
429:
1.1 cgd 430: case 't':
431: case 'T':
1.119 lukem 432: syslog(LOG_WARNING,
1.111 lukem 433: "-%c has been deprecated in favour of ftpd.conf",
434: ch);
435: break;
436:
1.1 cgd 437: case 'u':
1.111 lukem 438: doutmp = 1;
1.8 deraadt 439: break;
1.1 cgd 440:
1.78 lukem 441: case 'U':
1.111 lukem 442: doutmp = 0;
1.78 lukem 443: break;
444:
1.101 lukem 445: case 'V':
446: if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
447: version = NULL;
448: else
1.171 christos 449: version = ftpd_strdup(optarg);
1.101 lukem 450: break;
451:
1.111 lukem 452: case 'w':
453: dowtmp = 1;
454: break;
455:
1.102 lukem 456: case 'W':
457: dowtmp = 0;
458: break;
459:
1.118 lukem 460: case 'X':
1.154 lukem 461: doxferlog |= 1;
1.118 lukem 462: break;
463:
1.1 cgd 464: default:
1.35 lukem 465: if (optopt == 'a' || optopt == 'C')
466: exit(1);
1.119 lukem 467: syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
1.1 cgd 468: break;
469: }
470: }
1.111 lukem 471: if (EMPTYSTR(confdir))
472: confdir = _DEFAULT_CONFDIR;
1.35 lukem 473:
1.166 christos 474: if (dowtmp) {
475: #ifdef SUPPORT_UTMPX
476: ftpd_initwtmpx();
477: #endif
478: #ifdef SUPPORT_UTMP
479: ftpd_initwtmp();
480: #endif
481: }
1.157 lukem 482: errno = 0;
483: l = sysconf(_SC_LOGIN_NAME_MAX);
484: if (l == -1 && errno != 0) {
485: syslog(LOG_ERR, "sysconf _SC_LOGIN_NAME_MAX: %m");
486: exit(1);
487: } else if (l <= 0) {
488: syslog(LOG_WARNING, "using conservative LOGIN_NAME_MAX value");
489: curname_len = _POSIX_LOGIN_NAME_MAX;
490: } else
491: curname_len = (size_t)l;
492: curname = malloc(curname_len);
493: if (curname == NULL) {
494: syslog(LOG_ERR, "malloc: %m");
495: exit(1);
496: }
497: curname[0] = '\0';
498:
1.167 peter 499: if (Dflag) {
500: int error, fd, i, n, *socks;
501: struct pollfd *fds;
502: struct addrinfo hints, *res, *res0;
503:
504: if (daemon(1, 0) == -1) {
505: syslog(LOG_ERR, "failed to daemonize: %m");
506: exit(1);
507: }
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);
513:
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);
519: if (error) {
520: syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
521: exit(1);
522: }
523:
524: for (n = 0, res = res0; res != NULL; res = res->ai_next)
525: n++;
526: if (n == 0) {
527: syslog(LOG_ERR, "no addresses available");
528: exit(1);
529: }
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");
534: exit(1);
535: }
536:
537: for (n = 0, res = res0; res != NULL; res = res->ai_next) {
538: socks[n] = socket(res->ai_family, res->ai_socktype,
539: res->ai_protocol);
540: if (socks[n] == -1)
541: continue;
542: (void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR,
543: &on, sizeof(on));
544: if (bind(socks[n], res->ai_addr, res->ai_addrlen)
545: == -1) {
546: (void)close(socks[n]);
547: continue;
548: }
549: if (listen(socks[n], 12) == -1) {
550: (void)close(socks[n]);
551: continue;
552: }
553:
554: fds[n].fd = socks[n];
555: fds[n].events = POLLIN;
556: n++;
557: }
558: if (n == 0) {
559: syslog(LOG_ERR, "%m");
560: exit(1);
561: }
562: freeaddrinfo(res0);
563:
564: if (pidfile(NULL) == -1)
565: syslog(LOG_ERR, "failed to write a pid file: %m");
566:
567: for (;;) {
568: if (poll(fds, n, INFTIM) == -1) {
569: if (errno == EINTR)
570: continue;
571: syslog(LOG_ERR, "poll: %m");
572: exit(1);
573: }
574: for (i = 0; i < n; i++) {
575: if (fds[i].revents & POLLIN) {
576: fd = accept(fds[i].fd, NULL, NULL);
577: if (fd == -1) {
578: syslog(LOG_ERR, "accept: %m");
579: continue;
580: }
581: switch (fork()) {
582: case -1:
583: syslog(LOG_ERR, "fork: %m");
584: break;
585: case 0:
586: goto child;
587: /* NOTREACHED */
588: }
589: (void)close(fd);
590: }
591: }
592: }
593: child:
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]);
599: }
600:
1.118 lukem 601: memset((char *)&his_addr, 0, sizeof(his_addr));
1.109 lukem 602: addrlen = sizeof(his_addr.si_su);
603: if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
1.196 lukem 604: syslog((errno == ENOTCONN) ? LOG_NOTICE : LOG_ERR,
605: "getpeername (%s): %m",argv[0]);
1.35 lukem 606: exit(1);
607: }
1.109 lukem 608: his_addr.su_len = addrlen;
1.118 lukem 609: memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr));
1.109 lukem 610: addrlen = sizeof(ctrl_addr.si_su);
1.68 itojun 611: if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
612: syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
613: exit(1);
614: }
1.109 lukem 615: ctrl_addr.su_len = addrlen;
1.104 christos 616: #ifdef INET6
1.67 itojun 617: if (his_addr.su_family == AF_INET6
1.109 lukem 618: && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) {
1.68 itojun 619: #if 1
620: /*
621: * IPv4 control connection arrived to AF_INET6 socket.
622: * I hate to do this, but this is the easiest solution.
1.90 itojun 623: *
624: * The assumption is untrue on SIIT environment.
1.68 itojun 625: */
1.109 lukem 626: struct sockinet tmp_addr;
1.74 itojun 627: const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
1.68 itojun 628:
629: tmp_addr = his_addr;
630: memset(&his_addr, 0, sizeof(his_addr));
1.109 lukem 631: his_addr.su_family = AF_INET;
632: his_addr.su_len = sizeof(his_addr.si_su.su_sin);
633: memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
634: sizeof(his_addr.su_addr));
635: his_addr.su_port = tmp_addr.su_port;
1.68 itojun 636:
637: tmp_addr = ctrl_addr;
638: memset(&ctrl_addr, 0, sizeof(ctrl_addr));
1.109 lukem 639: ctrl_addr.su_family = AF_INET;
640: ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin);
641: memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
642: sizeof(ctrl_addr.su_addr));
643: ctrl_addr.su_port = tmp_addr.su_port;
1.68 itojun 644: #else
1.67 itojun 645: while (fgets(line, sizeof(line), fd) != NULL) {
646: if ((cp = strchr(line, '\n')) != NULL)
647: *cp = '\0';
1.95 lukem 648: reply(-530, "%s", line);
1.67 itojun 649: }
650: (void) fflush(stdout);
651: (void) fclose(fd);
652: reply(530,
1.101 lukem 653: "Connection from IPv4 mapped address is not supported.");
1.67 itojun 654: exit(0);
1.68 itojun 655: #endif
1.75 itojun 656:
657: mapped = 1;
658: } else
1.109 lukem 659: #endif /* INET6 */
1.75 itojun 660: mapped = 0;
1.35 lukem 661: #ifdef IP_TOS
1.75 itojun 662: if (!mapped && his_addr.su_family == AF_INET) {
1.67 itojun 663: tos = IPTOS_LOWDELAY;
664: if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
665: sizeof(int)) < 0)
666: syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
667: }
1.35 lukem 668: #endif
1.80 lukem 669: /* if the hostname hasn't been given, attempt to determine it */
670: if (hostname[0] == '\0') {
1.109 lukem 671: if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su,
1.180 christos 672: ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0,
673: getnameopts) != 0)
1.80 lukem 674: (void)gethostname(hostname, sizeof(hostname));
675: hostname[sizeof(hostname) - 1] = '\0';
676: }
1.79 lukem 677:
1.35 lukem 678: /* set this here so klogin can use it... */
679: (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
680:
1.1 cgd 681: (void) freopen(_PATH_DEVNULL, "w", stderr);
1.158 lukem 682:
683: memset(&sa, 0, sizeof(sa));
684: sa.sa_handler = SIG_DFL;
685: sa.sa_flags = SA_RESTART;
686: sigemptyset(&sa.sa_mask);
687: (void) sigaction(SIGCHLD, &sa, NULL);
688:
689: sa.sa_handler = sigquit;
690: sa.sa_flags = SA_RESTART;
691: sigfillset(&sa.sa_mask); /* block all sigs in these handlers */
692: (void) sigaction(SIGHUP, &sa, NULL);
693: (void) sigaction(SIGINT, &sa, NULL);
694: (void) sigaction(SIGQUIT, &sa, NULL);
695: (void) sigaction(SIGTERM, &sa, NULL);
696: sa.sa_handler = lostconn;
697: (void) sigaction(SIGPIPE, &sa, NULL);
698: sa.sa_handler = toolong;
699: (void) sigaction(SIGALRM, &sa, NULL);
700: sa.sa_handler = sigurg;
701: (void) sigaction(SIGURG, &sa, NULL);
1.1 cgd 702:
703: /* Try to handle urgent data inline */
704: #ifdef SO_OOBINLINE
705: if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
1.119 lukem 706: syslog(LOG_WARNING, "setsockopt: %m");
1.1 cgd 707: #endif
1.66 briggs 708: /* Set keepalives on the socket to detect dropped connections. */
709: #ifdef SO_KEEPALIVE
710: keepalive = 1;
711: if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive,
712: sizeof(int)) < 0)
713: syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
714: #endif
1.1 cgd 715:
716: #ifdef F_SETOWN
717: if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1.119 lukem 718: syslog(LOG_WARNING, "fcntl F_SETOWN: %m");
1.1 cgd 719: #endif
1.118 lukem 720: logremotehost(&his_addr);
1.1 cgd 721: /*
722: * Set up default state
723: */
724: data = -1;
725: type = TYPE_A;
726: form = FORM_N;
727: stru = STRU_F;
728: mode = MODE_S;
729: tmpline[0] = '\0';
1.57 lukem 730: hasyyerrored = 0;
1.8 deraadt 731:
1.38 mycroft 732: #ifdef KERBEROS5
733: kerror = krb5_init_context(&kcontext);
734: if (kerror) {
1.119 lukem 735: syslog(LOG_ERR, "%s when initializing Kerberos context",
1.38 mycroft 736: error_message(kerror));
737: exit(0);
738: }
1.45 christos 739: #endif /* KERBEROS5 */
1.38 mycroft 740:
1.82 lukem 741: init_curclass();
742: curclass.timeout = 300; /* 5 minutes, as per login(1) */
743: curclass.type = CLASS_REAL;
744:
1.8 deraadt 745: /* If logins are disabled, print out the message. */
1.100 lukem 746: if (display_file(_PATH_NOLOGIN, 530)) {
1.8 deraadt 747: reply(530, "System not available.");
748: exit(0);
749: }
1.163 christos 750: (void)display_file(conffilename(_NAME_FTPWELCOME), 220);
1.8 deraadt 751: /* reply(220,) must follow */
1.101 lukem 752: if (EMPTYSTR(version))
753: reply(220, "%s FTP server ready.", hostname);
754: else
755: reply(220, "%s FTP server (%s) ready.", hostname, version);
1.73 lukem 756:
1.154 lukem 757: if (xferlogname != NULL) {
758: xferlogfd = open(xferlogname, O_WRONLY | O_APPEND | O_CREAT,
759: 0660);
760: if (xferlogfd == -1)
761: syslog(LOG_WARNING, "open xferlog `%s': %m",
762: xferlogname);
763: else
764: doxferlog |= 2;
765: }
766:
1.123 aidan 767: ftp_loop();
1.1 cgd 768: /* NOTREACHED */
769: }
770:
1.8 deraadt 771: static void
1.178 christos 772: lostconn(int signo __unused)
1.1 cgd 773: {
1.8 deraadt 774:
1.171 christos 775: if (ftpd_debug)
1.1 cgd 776: syslog(LOG_DEBUG, "lost connection");
1.35 lukem 777: dologout(1);
1.1 cgd 778: }
779:
1.158 lukem 780: static void
1.178 christos 781: toolong(int signo __unused)
1.158 lukem 782: {
783:
784: /* XXXSIGRACE */
785: reply(421,
786: "Timeout (" LLF " seconds): closing control connection.",
787: (LLT)curclass.timeout);
788: if (logging)
789: syslog(LOG_INFO, "User %s timed out after " LLF " seconds",
790: (pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout);
791: dologout(1);
792: }
793:
794: static void
795: sigquit(int signo)
796: {
797:
1.171 christos 798: if (ftpd_debug)
1.158 lukem 799: syslog(LOG_DEBUG, "got signal %d", signo);
800: dologout(1);
801: }
802:
803: static void
1.178 christos 804: sigurg(int signo __unused)
1.158 lukem 805: {
806:
807: urgflag = 1;
808: }
809:
810:
1.1 cgd 811: /*
812: * Save the result of a getpwnam. Used for USER command, since
813: * the data returned must not be clobbered by any other command
814: * (e.g., globbing).
815: */
1.8 deraadt 816: static struct passwd *
1.88 lukem 817: sgetpwnam(const char *name)
1.1 cgd 818: {
819: static struct passwd save;
1.8 deraadt 820: struct passwd *p;
1.1 cgd 821:
822: if ((p = getpwnam(name)) == NULL)
823: return (p);
824: if (save.pw_name) {
1.54 mycroft 825: free((char *)save.pw_name);
1.78 lukem 826: memset(save.pw_passwd, 0, strlen(save.pw_passwd));
1.54 mycroft 827: free((char *)save.pw_passwd);
828: free((char *)save.pw_gecos);
829: free((char *)save.pw_dir);
830: free((char *)save.pw_shell);
1.1 cgd 831: }
832: save = *p;
1.171 christos 833: save.pw_name = ftpd_strdup(p->pw_name);
834: save.pw_passwd = ftpd_strdup(p->pw_passwd);
835: save.pw_gecos = ftpd_strdup(p->pw_gecos);
836: save.pw_dir = ftpd_strdup(p->pw_dir);
837: save.pw_shell = ftpd_strdup(p->pw_shell);
1.1 cgd 838: return (&save);
839: }
840:
1.93 lukem 841: static int login_attempts; /* number of failed login attempts */
1.133 lukem 842: static int askpasswd; /* had USER command, ask for PASSwd */
843: static int permitted; /* USER permitted */
1.1 cgd 844:
845: /*
846: * USER command.
847: * Sets global passwd pointer pw if named account exists and is acceptable;
848: * sets askpasswd if a PASS command is expected. If logged in previously,
849: * need to reset state. If name is "ftp" or "anonymous", the name is not in
1.163 christos 850: * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
1.1 cgd 851: * If account doesn't exist, ask for passwd anyway. Otherwise, check user
852: * requesting login privileges. Disallow anyone who does not have a standard
853: * shell as returned by getusershell(). Disallow anyone mentioned in the file
1.163 christos 854: * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
1.1 cgd 855: */
1.8 deraadt 856: void
1.88 lukem 857: user(const char *name)
1.1 cgd 858: {
1.133 lukem 859: char *class;
1.164 christos 860: #ifdef LOGIN_CAP
861: login_cap_t *lc = NULL;
862: #endif
1.183 lukem 863: #ifdef USE_PAM
864: int e;
865: #endif
1.133 lukem 866:
867: class = NULL;
1.1 cgd 868: if (logged_in) {
1.73 lukem 869: switch (curclass.type) {
870: case CLASS_GUEST:
1.1 cgd 871: reply(530, "Can't change user from guest login.");
872: return;
1.73 lukem 873: case CLASS_CHROOT:
1.5 cgd 874: reply(530, "Can't change user from chroot user.");
875: return;
1.73 lukem 876: case CLASS_REAL:
1.101 lukem 877: if (dropprivs) {
878: reply(530, "Can't change user.");
879: return;
880: }
1.73 lukem 881: end_login();
882: break;
883: default:
884: abort();
1.1 cgd 885: }
886: }
887:
1.92 explorer 888: #if defined(KERBEROS)
1.38 mycroft 889: kdestroy();
890: #endif
1.92 explorer 891: #if defined(KERBEROS5)
892: k5destroy();
893: #endif
1.38 mycroft 894:
1.73 lukem 895: curclass.type = CLASS_REAL;
1.133 lukem 896: askpasswd = 0;
897: permitted = 0;
898:
1.1 cgd 899: if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
1.81 lukem 900: /* need `pw' setup for checkaccess() and checkuser () */
901: if ((pw = sgetpwnam("ftp")) == NULL)
902: reply(530, "User %s unknown.", name);
1.82 lukem 903: else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
1.1 cgd 904: reply(530, "User %s access denied.", name);
1.81 lukem 905: else {
1.73 lukem 906: curclass.type = CLASS_GUEST;
1.1 cgd 907: askpasswd = 1;
1.8 deraadt 908: reply(331,
909: "Guest login ok, type your name as password.");
1.81 lukem 910: }
1.133 lukem 911: if (!askpasswd) {
912: if (logging)
913: syslog(LOG_NOTICE,
914: "ANONYMOUS FTP LOGIN REFUSED FROM %s",
1.191 christos 915: remoteloghost);
1.133 lukem 916: end_login();
917: goto cleanup_user;
918: }
919: name = "ftp";
920: } else
921: pw = sgetpwnam(name);
1.34 lukem 922:
1.157 lukem 923: strlcpy(curname, name, curname_len);
1.36 mycroft 924:
1.133 lukem 925: /* check user in /etc/ftpusers, and setup class */
1.163 christos 926: permitted = checkuser(_NAME_FTPUSERS, curname, 1, 0, &class);
1.133 lukem 927:
928: /* check user in /etc/ftpchroot */
1.177 lukem 929: #ifdef LOGIN_CAP
1.164 christos 930: lc = login_getpwclass(pw);
1.177 lukem 931: #endif
1.164 christos 932: if (checkuser(_NAME_FTPCHROOT, curname, 0, 0, NULL)
933: #ifdef LOGIN_CAP /* Allow login.conf configuration as well */
934: || login_getcapbool(lc, "ftp-chroot", 0)
935: #endif
936: ) {
1.133 lukem 937: if (curclass.type == CLASS_GUEST) {
938: syslog(LOG_NOTICE,
939: "Can't change guest user to chroot class; remove entry in %s",
1.163 christos 940: _NAME_FTPCHROOT);
1.133 lukem 941: exit(1);
942: }
943: curclass.type = CLASS_CHROOT;
944: }
1.183 lukem 945:
1.133 lukem 946: /* determine default class */
947: if (class == NULL) {
948: switch (curclass.type) {
949: case CLASS_GUEST:
1.171 christos 950: class = ftpd_strdup("guest");
1.133 lukem 951: break;
952: case CLASS_CHROOT:
1.171 christos 953: class = ftpd_strdup("chroot");
1.133 lukem 954: break;
955: case CLASS_REAL:
1.171 christos 956: class = ftpd_strdup("real");
1.133 lukem 957: break;
958: default:
959: syslog(LOG_ERR, "unknown curclass.type %d; aborting",
960: curclass.type);
961: abort();
962: }
963: }
964: /* parse ftpd.conf, setting up various parameters */
965: parse_conf(class);
966: /* if not guest user, check for valid shell */
967: if (pw == NULL)
968: permitted = 0;
969: else {
970: const char *cp, *shell;
971:
972: if ((shell = pw->pw_shell) == NULL || *shell == 0)
973: shell = _PATH_BSHELL;
974: while ((cp = getusershell()) != NULL)
975: if (strcmp(cp, shell) == 0)
976: break;
977: endusershell();
978: if (cp == NULL && curclass.type != CLASS_GUEST)
979: permitted = 0;
980: }
981:
982: /* deny quickly (after USER not PASS) if requested */
983: if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) {
984: reply(530, "User %s may not use FTP.", curname);
985: if (logging)
986: syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
1.191 christos 987: remoteloghost, curname);
1.133 lukem 988: end_login();
989: goto cleanup_user;
990: }
991:
992: /* if haven't asked yet (i.e, not anon), ask now */
993: if (!askpasswd) {
994: askpasswd = 1;
1.183 lukem 995: #ifdef USE_PAM
996: e = auth_pam(); /* this does reply(331, ...) */
997: do_pass(1, e, "");
998: goto cleanup_user;
999: #else /* !USE_PAM */
1.7 deraadt 1000: #ifdef SKEY
1.133 lukem 1001: if (skey_haskey(curname) == 0) {
1002: const char *myskey;
1.7 deraadt 1003:
1.133 lukem 1004: myskey = skey_keyinfo(curname);
1.134 lukem 1005: reply(331, "Password [ %s ] required for %s.",
1.133 lukem 1006: myskey ? myskey : "error getting challenge",
1007: curname);
1008: } else
1.7 deraadt 1009: #endif
1.133 lukem 1010: reply(331, "Password required for %s.", curname);
1.183 lukem 1011: #endif /* !USE_PAM */
1.133 lukem 1012: }
1.7 deraadt 1013:
1.133 lukem 1014: cleanup_user:
1.164 christos 1015: #ifdef LOGIN_CAP
1016: login_close(lc);
1017: #endif
1.1 cgd 1018: /*
1019: * Delay before reading passwd after first failed
1020: * attempt to slow down passwd-guessing programs.
1021: */
1022: if (login_attempts)
1023: sleep((unsigned) login_attempts);
1.133 lukem 1024:
1025: if (class)
1026: free(class);
1.1 cgd 1027: }
1028:
1029: /*
1.58 lukem 1030: * Determine whether something is to happen (allow access, chroot)
1031: * for a user. Each line is a shell-style glob followed by
1032: * `yes' or `no'.
1033: *
1.126 wiz 1034: * For backward compatibility, `allow' and `deny' are synonymns
1.58 lukem 1035: * for `yes' and `no', respectively.
1.19 cjs 1036: *
1.21 cjs 1037: * Each glob is matched against the username in turn, and the first
1.58 lukem 1038: * match found is used. If no match is found, the result is the
1039: * argument `def'. If a match is found but without and explicit
1040: * `yes'/`no', the result is the opposite of def.
1041: *
1042: * If the file doesn't exist at all, the result is the argument
1043: * `nofile'
1.21 cjs 1044: *
1045: * Any line starting with `#' is considered a comment and ignored.
1.19 cjs 1046: *
1.73 lukem 1047: * Returns 0 if the user is denied, or 1 if they are allowed.
1.81 lukem 1048: *
1049: * NOTE: needs struct passwd *pw setup before use.
1.19 cjs 1050: */
1.118 lukem 1051: static int
1.88 lukem 1052: checkuser(const char *fname, const char *name, int def, int nofile,
1053: char **retclass)
1.58 lukem 1054: {
1055: FILE *fd;
1056: int retval;
1.132 lukem 1057: char *word, *perm, *class, *buf, *p;
1.76 lukem 1058: size_t len, line;
1.58 lukem 1059:
1060: retval = def;
1.73 lukem 1061: if (retclass != NULL)
1062: *retclass = NULL;
1.58 lukem 1063: if ((fd = fopen(conffilename(fname), "r")) == NULL)
1064: return nofile;
1.19 cjs 1065:
1.76 lukem 1066: line = 0;
1.73 lukem 1067: for (;
1.76 lukem 1068: (buf = fparseln(fd, &len, &line, NULL, FPARSELN_UNESCCOMM |
1.147 lukem 1069: FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
1.73 lukem 1070: free(buf), buf = NULL) {
1.132 lukem 1071: word = perm = class = NULL;
1.73 lukem 1072: p = buf;
1073: if (len < 1)
1074: continue;
1075: if (p[len - 1] == '\n')
1076: p[--len] = '\0';
1077: if (EMPTYSTR(p))
1.19 cjs 1078: continue;
1.73 lukem 1079:
1.132 lukem 1080: NEXTWORD(p, word);
1.73 lukem 1081: NEXTWORD(p, perm);
1082: NEXTWORD(p, class);
1.132 lukem 1083: if (EMPTYSTR(word))
1.70 tron 1084: continue;
1.76 lukem 1085: if (!EMPTYSTR(class)) {
1086: if (strcasecmp(class, "all") == 0 ||
1087: strcasecmp(class, "none") == 0) {
1088: syslog(LOG_WARNING,
1089: "%s line %d: illegal user-defined class `%s' - skipping entry",
1090: fname, (int)line, class);
1091: continue;
1092: }
1093: }
1.73 lukem 1094:
1095: /* have a host specifier */
1.132 lukem 1096: if ((p = strchr(word, '@')) != NULL) {
1.188 lukem 1097: unsigned char net[16], mask[16], *addr;
1098: int addrlen, bits, bytes, a;
1.73 lukem 1099:
1100: *p++ = '\0';
1101: /* check against network or CIDR */
1.188 lukem 1102: memset(net, 0x00, sizeof(net));
1103: if ((bits = inet_net_pton(his_addr.su_family, p, net,
1104: sizeof(net))) != -1) {
1105: #ifdef INET6
1106: if (his_addr.su_family == AF_INET) {
1107: #endif
1108: addrlen = 4;
1109: addr = (unsigned char *)&his_addr.su_addr;
1110: #ifdef INET6
1111: } else {
1112: addrlen = 16;
1113: addr = (unsigned char *)&his_addr.su_6addr;
1114: }
1115: #endif
1116: bytes = bits / 8;
1117: bits = bits % 8;
1118: if (bytes > 0)
1119: memset(mask, 0xFF, bytes);
1120: if (bytes < addrlen)
1121: mask[bytes] = 0xFF << (8 - bits);
1122: if (bytes + 1 < addrlen)
1123: memset(mask + bytes + 1, 0x00,
1124: addrlen - bytes - 1);
1125: for (a = 0; a < addrlen; a++)
1126: if ((addr[a] & mask[a]) != net[a])
1127: break;
1128: if (a < addrlen)
1.73 lukem 1129: continue;
1130:
1131: /* check against hostname glob */
1.132 lukem 1132: } else if (fnmatch(p, remotehost, FNM_CASEFOLD) != 0)
1.73 lukem 1133: continue;
1.19 cjs 1134: }
1.73 lukem 1135:
1.81 lukem 1136: /* have a group specifier */
1.132 lukem 1137: if ((p = strchr(word, ':')) != NULL) {
1.81 lukem 1138: gid_t *groups, *ng;
1139: int gsize, i, found;
1140:
1.133 lukem 1141: if (pw == NULL)
1142: continue; /* no match for unknown user */
1.81 lukem 1143: *p++ = '\0';
1144: groups = NULL;
1145: gsize = 16;
1146: do {
1147: ng = realloc(groups, gsize * sizeof(gid_t));
1148: if (ng == NULL)
1149: fatal(
1150: "Local resource failure: realloc");
1151: groups = ng;
1152: } while (getgrouplist(pw->pw_name, pw->pw_gid,
1153: groups, &gsize) == -1);
1154: found = 0;
1155: for (i = 0; i < gsize; i++) {
1156: struct group *g;
1157:
1158: if ((g = getgrgid(groups[i])) == NULL)
1159: continue;
1160: if (fnmatch(p, g->gr_name, 0) == 0) {
1161: found = 1;
1162: break;
1163: }
1164: }
1165: free(groups);
1166: if (!found)
1167: continue;
1168: }
1169:
1.73 lukem 1170: /* check against username glob */
1.132 lukem 1171: if (fnmatch(word, name, 0) != 0)
1.73 lukem 1172: continue;
1173:
1174: if (perm != NULL &&
1175: ((strcasecmp(perm, "allow") == 0) ||
1176: (strcasecmp(perm, "yes") == 0)))
1177: retval = 1;
1178: else if (perm != NULL &&
1179: ((strcasecmp(perm, "deny") == 0) ||
1180: (strcasecmp(perm, "no") == 0)))
1181: retval = 0;
1182: else
1183: retval = !def;
1184: if (!EMPTYSTR(class) && retclass != NULL)
1.171 christos 1185: *retclass = ftpd_strdup(class);
1.73 lukem 1186: free(buf);
1187: break;
1.19 cjs 1188: }
1189: (void) fclose(fd);
1.21 cjs 1190: return (retval);
1.19 cjs 1191: }
1.58 lukem 1192:
1193: /*
1194: * Check if user is allowed by /etc/ftpusers
1.73 lukem 1195: * returns 1 for yes, 0 for no
1.81 lukem 1196: *
1197: * NOTE: needs struct passwd *pw setup (for checkuser())
1.58 lukem 1198: */
1.118 lukem 1199: static int
1.88 lukem 1200: checkaccess(const char *name)
1.58 lukem 1201: {
1202:
1.163 christos 1203: return (checkuser(_NAME_FTPUSERS, name, 1, 0, NULL));
1.58 lukem 1204: }
1.19 cjs 1205:
1.140 christos 1206: static void
1.160 christos 1207: login_utmp(const char *line, const char *name, const char *host,
1208: struct sockinet *haddr)
1.140 christos 1209: {
1210: #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1211: struct timeval tv;
1212: (void)gettimeofday(&tv, NULL);
1213: #endif
1214: #ifdef SUPPORT_UTMPX
1215: if (doutmp) {
1216: (void)memset(&utmpx, 0, sizeof(utmpx));
1217: utmpx.ut_tv = tv;
1218: utmpx.ut_pid = getpid();
1219: utmpx.ut_id[0] = 'f';
1220: utmpx.ut_id[1] = 't';
1221: utmpx.ut_id[2] = 'p';
1222: utmpx.ut_id[3] = '*';
1223: utmpx.ut_type = USER_PROCESS;
1.144 itojun 1224: (void)strncpy(utmpx.ut_name, name, sizeof(utmpx.ut_name));
1225: (void)strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
1226: (void)strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
1.160 christos 1227: (void)memcpy(&utmpx.ut_ss, &haddr->si_su, haddr->su_len);
1.155 tacha 1228: ftpd_loginx(&utmpx);
1.140 christos 1229: }
1230: if (dowtmp)
1.160 christos 1231: ftpd_logwtmpx(line, name, host, haddr, 0, USER_PROCESS);
1.140 christos 1232: #endif
1233: #ifdef SUPPORT_UTMP
1234: if (doutmp) {
1235: (void)memset(&utmp, 0, sizeof(utmp));
1236: (void)time(&utmp.ut_time);
1.144 itojun 1237: (void)strncpy(utmp.ut_name, name, sizeof(utmp.ut_name));
1238: (void)strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
1239: (void)strncpy(utmp.ut_host, host, sizeof(utmp.ut_host));
1.152 lukem 1240: ftpd_login(&utmp);
1.140 christos 1241: }
1242: if (dowtmp)
1.152 lukem 1243: ftpd_logwtmp(line, name, host);
1.140 christos 1244: #endif
1245: }
1246:
1247: static void
1248: logout_utmp(void)
1249: {
1250: #ifdef SUPPORT_UTMPX
1.166 christos 1251: int okwtmpx = dowtmp;
1.140 christos 1252: #endif
1253: #ifdef SUPPORT_UTMP
1.166 christos 1254: int okwtmp = dowtmp;
1.140 christos 1255: #endif
1.166 christos 1256: if (logged_in) {
1.140 christos 1257: #ifdef SUPPORT_UTMPX
1.166 christos 1258: if (doutmp)
1259: okwtmpx &= ftpd_logoutx(ttyline, 0, DEAD_PROCESS);
1260: if (okwtmpx)
1261: ftpd_logwtmpx(ttyline, "", "", NULL, 0, DEAD_PROCESS);
1.140 christos 1262: #endif
1263: #ifdef SUPPORT_UTMP
1.166 christos 1264: if (doutmp)
1265: okwtmp &= ftpd_logout(ttyline);
1266: if (okwtmp)
1.152 lukem 1267: ftpd_logwtmp(ttyline, "", "");
1.140 christos 1268: #endif
1269: }
1270: }
1271:
1.19 cjs 1272: /*
1.118 lukem 1273: * Terminate login as previous user (if any), resetting state;
1.1 cgd 1274: * used when USER command is given or login fails.
1275: */
1.8 deraadt 1276: static void
1.88 lukem 1277: end_login(void)
1.1 cgd 1278: {
1.164 christos 1279: #ifdef USE_PAM
1280: int e;
1281: #endif
1.140 christos 1282: logout_utmp();
1.118 lukem 1283: show_chdir_messages(-1); /* flush chdir cache */
1284: if (pw != NULL && pw->pw_passwd != NULL)
1285: memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
1.1 cgd 1286: pw = NULL;
1287: logged_in = 0;
1.133 lukem 1288: askpasswd = 0;
1289: permitted = 0;
1.85 lukem 1290: quietmessages = 0;
1.95 lukem 1291: gidcount = 0;
1.73 lukem 1292: curclass.type = CLASS_REAL;
1.118 lukem 1293: (void) seteuid((uid_t)0);
1.164 christos 1294: #ifdef LOGIN_CAP
1295: setusercontext(NULL, getpwuid(0), 0,
1296: LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1297: #endif
1298: #ifdef USE_PAM
1299: if (pamh) {
1300: if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1301: syslog(LOG_ERR, "pam_setcred: %s",
1302: pam_strerror(pamh, e));
1303: if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1304: syslog(LOG_ERR, "pam_close_session: %s",
1305: pam_strerror(pamh, e));
1306: if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1307: syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1308: pamh = NULL;
1309: }
1310: #endif
1.1 cgd 1311: }
1312:
1.8 deraadt 1313: void
1.88 lukem 1314: pass(const char *passwd)
1.1 cgd 1315: {
1.183 lukem 1316: do_pass(0, 0, passwd);
1317: }
1318:
1319: /*
1320: * Perform the passwd confirmation and login.
1321: *
1322: * If pass_checked is zero, confirm passwd is correct, & ignore pass_rval.
1323: * This is the traditional PASS implementation.
1324: *
1325: * If pass_checked is non-zero, use pass_rval and ignore passwd.
1326: * This is used by auth_pam() which has already parsed PASS.
1327: * This only applies to curclass.type != CLASS_GUEST.
1328: */
1329: static void
1330: do_pass(int pass_checked, int pass_rval, const char *passwd)
1331: {
1.78 lukem 1332: int rval;
1.133 lukem 1333: char root[MAXPATHLEN];
1.164 christos 1334: #ifdef LOGIN_CAP
1335: login_cap_t *lc = NULL;
1336: #endif
1337: #ifdef USE_PAM
1338: int e;
1339: #endif
1.183 lukem 1340:
1341: rval = 1;
1342:
1.1 cgd 1343: if (logged_in || askpasswd == 0) {
1344: reply(503, "Login with USER first.");
1345: return;
1346: }
1347: askpasswd = 0;
1.73 lukem 1348: if (curclass.type != CLASS_GUEST) {
1.101 lukem 1349: /* "ftp" is the only account allowed with no password */
1.8 deraadt 1350: if (pw == NULL) {
1351: rval = 1; /* failure below */
1352: goto skip;
1353: }
1.183 lukem 1354: if (pass_checked) { /* password validated in user() */
1355: rval = pass_rval;
1.164 christos 1356: goto skip;
1357: }
1.183 lukem 1358: #ifdef USE_PAM
1359: syslog(LOG_ERR, "do_pass: USE_PAM shouldn't get here");
1360: rval = 1;
1.164 christos 1361: goto skip;
1362: #endif
1.92 explorer 1363: #if defined(KERBEROS)
1.64 thorpej 1364: if (klogin(pw, "", hostname, (char *)passwd) == 0) {
1.92 explorer 1365: rval = 0;
1366: goto skip;
1367: }
1368: #endif
1369: #if defined(KERBEROS5)
1370: if (k5login(pw, "", hostname, (char *)passwd) == 0) {
1.37 mycroft 1371: rval = 0;
1372: goto skip;
1373: }
1374: #endif
1.36 mycroft 1375: #ifdef SKEY
1.62 lukem 1376: if (skey_haskey(pw->pw_name) == 0) {
1377: char *p;
1378: int r;
1379:
1.171 christos 1380: p = ftpd_strdup(passwd);
1.62 lukem 1381: r = skey_passcheck(pw->pw_name, p);
1382: free(p);
1383: if (r != -1) {
1384: rval = 0;
1385: goto skip;
1386: }
1.36 mycroft 1387: }
1.4 cgd 1388: #endif
1.116 lukem 1389: if (!sflag)
1390: rval = checkpassword(pw, passwd);
1391: else
1392: rval = 1;
1.4 cgd 1393:
1.89 lukem 1394: skip:
1.101 lukem 1395:
1396: /*
1397: * If rval > 0, the user failed the authentication check
1398: * above. If rval == 0, either Kerberos or local
1399: * authentication succeeded.
1400: */
1.4 cgd 1401: if (rval) {
1.98 sommerfe 1402: reply(530, "%s", rval == 2 ? "Password expired." :
1.45 christos 1403: "Login incorrect.");
1.23 lukem 1404: if (logging) {
1.8 deraadt 1405: syslog(LOG_NOTICE,
1.191 christos 1406: "FTP LOGIN FAILED FROM %s", remoteloghost);
1.23 lukem 1407: syslog(LOG_AUTHPRIV | LOG_NOTICE,
1.8 deraadt 1408: "FTP LOGIN FAILED FROM %s, %s",
1.191 christos 1409: remoteloghost, curname);
1.23 lukem 1410: }
1.1 cgd 1411: pw = NULL;
1412: if (login_attempts++ >= 5) {
1413: syslog(LOG_NOTICE,
1414: "repeated login failures from %s",
1.191 christos 1415: remoteloghost);
1.1 cgd 1416: exit(0);
1417: }
1418: return;
1419: }
1420: }
1.19 cjs 1421:
1.133 lukem 1422: /* password ok; check if anything else prevents login */
1423: if (! permitted) {
1.19 cjs 1424: reply(530, "User %s may not use FTP.", pw->pw_name);
1425: if (logging)
1.62 lukem 1426: syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
1.191 christos 1427: remoteloghost, pw->pw_name);
1.82 lukem 1428: goto bad;
1.19 cjs 1429: }
1430:
1.1 cgd 1431: login_attempts = 0; /* this time successful */
1.8 deraadt 1432: if (setegid((gid_t)pw->pw_gid) < 0) {
1433: reply(550, "Can't set gid.");
1.82 lukem 1434: goto bad;
1.8 deraadt 1435: }
1.164 christos 1436: #ifdef LOGIN_CAP
1437: if ((lc = login_getpwclass(pw)) != NULL) {
1438: #ifdef notyet
1439: char remote_ip[NI_MAXHOST];
1440:
1441: if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1442: remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1443: NI_NUMERICHOST))
1444: *remote_ip = 0;
1445: remote_ip[sizeof(remote_ip) - 1] = 0;
1446: if (!auth_hostok(lc, remotehost, remote_ip)) {
1447: syslog(LOG_INFO|LOG_AUTH,
1448: "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1449: pw->pw_name);
1450: reply(530, "Permission denied.");
1451: pw = NULL;
1452: return;
1453: }
1454: if (!auth_timeok(lc, time(NULL))) {
1455: reply(530, "Login not available right now.");
1456: pw = NULL;
1457: return;
1458: }
1459: #endif
1460: }
1461: setsid();
1462: setusercontext(lc, pw, 0,
1463: LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY|
1464: LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1465: #else
1.1 cgd 1466: (void) initgroups(pw->pw_name, pw->pw_gid);
1.101 lukem 1467: /* cache groups for cmds.c::matchgroup() */
1.164 christos 1468: #endif
1469: #ifdef USE_PAM
1470: if (pamh) {
1471: if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
1472: syslog(LOG_ERR, "pam_open_session: %s",
1473: pam_strerror(pamh, e));
1474: } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
1475: != PAM_SUCCESS) {
1476: syslog(LOG_ERR, "pam_setcred: %s",
1477: pam_strerror(pamh, e));
1478: }
1479: }
1480: #endif
1.151 dsl 1481: gidcount = getgroups(0, NULL);
1482: if (gidlist)
1483: free(gidlist);
1484: gidlist = malloc(gidcount * sizeof *gidlist);
1485: gidcount = getgroups(gidcount, gidlist);
1.1 cgd 1486:
1.140 christos 1487: /* open utmp/wtmp before chroot */
1.160 christos 1488: login_utmp(ttyline, pw->pw_name, remotehost, &his_addr);
1.78 lukem 1489:
1.1 cgd 1490: logged_in = 1;
1491:
1.111 lukem 1492: connections = 1;
1493: if (dopidfile)
1494: count_users();
1.82 lukem 1495: if (curclass.limit != -1 && connections > curclass.limit) {
1496: if (! EMPTYSTR(curclass.limitfile))
1.100 lukem 1497: (void)display_file(conffilename(curclass.limitfile),
1498: 530);
1.82 lukem 1499: reply(530,
1.147 lukem 1500: "User %s access denied, connection limit of " LLF
1501: " reached.",
1502: pw->pw_name, (LLT)curclass.limit);
1.82 lukem 1503: syslog(LOG_NOTICE,
1.147 lukem 1504: "Maximum connection limit of " LLF
1505: " for class %s reached, login refused for %s",
1506: (LLT)curclass.limit, curclass.classname, pw->pw_name);
1.82 lukem 1507: goto bad;
1508: }
1.24 lukem 1509:
1.100 lukem 1510: homedir[0] = '/';
1.73 lukem 1511: switch (curclass.type) {
1512: case CLASS_GUEST:
1.101 lukem 1513: /*
1514: * We MUST do a chdir() after the chroot. Otherwise
1515: * the old current directory will be accessible as "."
1516: * outside the new root!
1517: */
1.100 lukem 1518: format_path(root,
1519: curclass.chroot ? curclass.chroot :
1520: anondir ? anondir :
1521: pw->pw_dir);
1522: format_path(homedir,
1523: curclass.homedir ? curclass.homedir :
1524: "/");
1525: if (EMPTYSTR(homedir))
1526: homedir[0] = '/';
1527: if (EMPTYSTR(root) || chroot(root) < 0) {
1528: syslog(LOG_NOTICE,
1529: "GUEST user %s: can't chroot to %s: %m",
1530: pw->pw_name, root);
1531: goto bad_guest;
1532: }
1533: if (chdir(homedir) < 0) {
1534: syslog(LOG_NOTICE,
1535: "GUEST user %s: can't chdir to %s: %m",
1536: pw->pw_name, homedir);
1537: bad_guest:
1.1 cgd 1538: reply(550, "Can't set guest privileges.");
1.5 cgd 1539: goto bad;
1540: }
1.73 lukem 1541: break;
1542: case CLASS_CHROOT:
1.100 lukem 1543: format_path(root,
1544: curclass.chroot ? curclass.chroot :
1545: pw->pw_dir);
1546: format_path(homedir,
1547: curclass.homedir ? curclass.homedir :
1548: "/");
1549: if (EMPTYSTR(homedir))
1550: homedir[0] = '/';
1551: if (EMPTYSTR(root) || chroot(root) < 0) {
1552: syslog(LOG_NOTICE,
1553: "CHROOT user %s: can't chroot to %s: %m",
1554: pw->pw_name, root);
1555: goto bad_chroot;
1556: }
1557: if (chdir(homedir) < 0) {
1558: syslog(LOG_NOTICE,
1559: "CHROOT user %s: can't chdir to %s: %m",
1560: pw->pw_name, homedir);
1561: bad_chroot:
1.5 cgd 1562: reply(550, "Can't change root.");
1.1 cgd 1563: goto bad;
1564: }
1.73 lukem 1565: break;
1566: case CLASS_REAL:
1.172 wiz 1567: /* only chroot REAL if explicitly requested */
1.118 lukem 1568: if (! EMPTYSTR(curclass.chroot)) {
1569: format_path(root, curclass.chroot);
1570: if (EMPTYSTR(root) || chroot(root) < 0) {
1571: syslog(LOG_NOTICE,
1572: "REAL user %s: can't chroot to %s: %m",
1573: pw->pw_name, root);
1574: goto bad_chroot;
1575: }
1576: }
1.100 lukem 1577: format_path(homedir,
1578: curclass.homedir ? curclass.homedir :
1579: pw->pw_dir);
1580: if (EMPTYSTR(homedir) || chdir(homedir) < 0) {
1.73 lukem 1581: if (chdir("/") < 0) {
1.100 lukem 1582: syslog(LOG_NOTICE,
1583: "REAL user %s: can't chdir to %s: %m",
1584: pw->pw_name,
1585: !EMPTYSTR(homedir) ? homedir : "/");
1.73 lukem 1586: reply(530,
1587: "User %s: can't change directory to %s.",
1.100 lukem 1588: pw->pw_name,
1589: !EMPTYSTR(homedir) ? homedir : "/");
1.73 lukem 1590: goto bad;
1.100 lukem 1591: } else {
1.95 lukem 1592: reply(-230,
1.73 lukem 1593: "No directory! Logging in with home=/");
1.100 lukem 1594: homedir[0] = '/';
1595: }
1596: }
1.73 lukem 1597: break;
1598: }
1.164 christos 1599: #ifndef LOGIN_CAP
1.151 dsl 1600: setsid();
1.105 jdolecek 1601: setlogin(pw->pw_name);
1.164 christos 1602: #endif
1.101 lukem 1603: if (dropprivs ||
1604: (curclass.type != CLASS_REAL &&
1605: ntohs(ctrl_addr.su_port) > IPPORT_RESERVED + 1)) {
1606: dropprivs++;
1607: if (setgid((gid_t)pw->pw_gid) < 0) {
1608: reply(550, "Can't set gid.");
1609: goto bad;
1610: }
1611: if (setuid((uid_t)pw->pw_uid) < 0) {
1612: reply(550, "Can't set uid.");
1613: goto bad;
1614: }
1615: } else {
1616: if (seteuid((uid_t)pw->pw_uid) < 0) {
1617: reply(550, "Can't set uid.");
1618: goto bad;
1619: }
1.1 cgd 1620: }
1.100 lukem 1621: setenv("HOME", homedir, 1);
1.44 lukem 1622:
1.85 lukem 1623: if (curclass.type == CLASS_GUEST && passwd[0] == '-')
1624: quietmessages = 1;
1625:
1.101 lukem 1626: /*
1627: * Display a login message, if it exists.
1628: * N.B. reply(230,) must follow the message.
1629: */
1.131 lukem 1630: if (! EMPTYSTR(curclass.motd))
1631: (void)display_file(conffilename(curclass.motd), 230);
1.24 lukem 1632: show_chdir_messages(230);
1.73 lukem 1633: if (curclass.type == CLASS_GUEST) {
1.118 lukem 1634: char *p;
1635:
1.1 cgd 1636: reply(230, "Guest login ok, access restrictions apply.");
1.181 lukem 1637: #if defined(HAVE_SETPROCTITLE)
1.8 deraadt 1638: snprintf(proctitle, sizeof(proctitle),
1.129 lukem 1639: "%s: anonymous/%s", remotehost, passwd);
1.97 itojun 1640: setproctitle("%s", proctitle);
1.181 lukem 1641: #endif /* defined(HAVE_SETPROCTITLE) */
1.1 cgd 1642: if (logging)
1.81 lukem 1643: syslog(LOG_INFO,
1644: "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1.191 christos 1645: remoteloghost, passwd,
1.81 lukem 1646: curclass.classname, CURCLASSTYPE);
1.118 lukem 1647: /* store guest password reply into pw_passwd */
1.171 christos 1648: REASSIGN(pw->pw_passwd, ftpd_strdup(passwd));
1.118 lukem 1649: for (p = pw->pw_passwd; *p; p++)
1.159 dsl 1650: if (!isgraph((unsigned char)*p))
1.118 lukem 1651: *p = '_';
1.1 cgd 1652: } else {
1653: reply(230, "User %s logged in.", pw->pw_name);
1.181 lukem 1654: #if defined(HAVE_SETPROCTITLE)
1.8 deraadt 1655: snprintf(proctitle, sizeof(proctitle),
1656: "%s: %s", remotehost, pw->pw_name);
1.97 itojun 1657: setproctitle("%s", proctitle);
1.181 lukem 1658: #endif /* defined(HAVE_SETPROCTITLE) */
1.1 cgd 1659: if (logging)
1.81 lukem 1660: syslog(LOG_INFO,
1661: "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1.191 christos 1662: remoteloghost, pw->pw_name,
1.81 lukem 1663: curclass.classname, CURCLASSTYPE);
1.1 cgd 1664: }
1.24 lukem 1665: (void) umask(curclass.umask);
1.164 christos 1666: #ifdef LOGIN_CAP
1667: login_close(lc);
1668: #endif
1.133 lukem 1669: return;
1.111 lukem 1670:
1.73 lukem 1671: bad:
1.164 christos 1672: #ifdef LOGIN_CAP
1673: login_close(lc);
1674: #endif
1.101 lukem 1675: /* Forget all about it... */
1.1 cgd 1676: end_login();
1677: }
1678:
1.8 deraadt 1679: void
1.193 lukem 1680: retrieve(const char *argv[], const char *name)
1.1 cgd 1681: {
1.95 lukem 1682: FILE *fin, *dout;
1.1 cgd 1683: struct stat st;
1.88 lukem 1684: int (*closefunc)(FILE *) = NULL;
1.148 thorpej 1685: int dolog, sendrv, closerv, stderrfd, isconversion, isdata, isls;
1.62 lukem 1686: struct timeval start, finish, td, *tdp;
1.139 enami 1687: struct rusage rusage_before, rusage_after;
1.62 lukem 1688: const char *dispname;
1.193 lukem 1689: const char *error;
1.1 cgd 1690:
1.49 lukem 1691: sendrv = closerv = stderrfd = -1;
1.148 thorpej 1692: isconversion = isdata = isls = dolog = 0;
1.62 lukem 1693: tdp = NULL;
1694: dispname = name;
1.95 lukem 1695: fin = dout = NULL;
1.138 lukem 1696: error = NULL;
1.118 lukem 1697: if (argv == NULL) { /* if not running a command ... */
1.148 thorpej 1698: dolog = 1;
1.62 lukem 1699: isdata = 1;
1700: fin = fopen(name, "r");
1701: closefunc = fclose;
1.118 lukem 1702: if (fin == NULL) /* doesn't exist?; try a conversion */
1.71 lukem 1703: argv = do_conversion(name);
1704: if (argv != NULL) {
1.49 lukem 1705: isconversion++;
1.118 lukem 1706: syslog(LOG_DEBUG, "get command: '%s' on '%s'",
1.71 lukem 1707: argv[0], name);
1.49 lukem 1708: }
1.24 lukem 1709: }
1.71 lukem 1710: if (argv != NULL) {
1.82 lukem 1711: char temp[MAXPATHLEN];
1.1 cgd 1712:
1.71 lukem 1713: if (strcmp(argv[0], INTERNAL_LS) == 0) {
1.63 lukem 1714: isls = 1;
1715: stderrfd = -1;
1716: } else {
1717: (void)snprintf(temp, sizeof(temp), "%s", TMPFILE);
1718: stderrfd = mkstemp(temp);
1719: if (stderrfd != -1)
1720: (void)unlink(temp);
1721: }
1.71 lukem 1722: dispname = argv[0];
1723: fin = ftpd_popen(argv, "r", stderrfd);
1.62 lukem 1724: closefunc = ftpd_pclose;
1.1 cgd 1725: st.st_size = -1;
1726: st.st_blksize = BUFSIZ;
1727: }
1728: if (fin == NULL) {
1.8 deraadt 1729: if (errno != 0) {
1.62 lukem 1730: perror_reply(550, dispname);
1.148 thorpej 1731: if (dolog)
1.118 lukem 1732: logxfer("get", -1, name, NULL, NULL,
1.62 lukem 1733: strerror(errno));
1.8 deraadt 1734: }
1.71 lukem 1735: goto cleanupretrieve;
1.1 cgd 1736: }
1.8 deraadt 1737: byte_count = -1;
1.71 lukem 1738: if (argv == NULL
1.49 lukem 1739: && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1.138 lukem 1740: error = "Not a plain file";
1741: reply(550, "%s: %s.", dispname, error);
1.1 cgd 1742: goto done;
1743: }
1744: if (restart_point) {
1745: if (type == TYPE_A) {
1.49 lukem 1746: off_t i;
1.8 deraadt 1747: int c;
1.1 cgd 1748:
1.49 lukem 1749: for (i = 0; i < restart_point; i++) {
1.1 cgd 1750: if ((c=getc(fin)) == EOF) {
1.138 lukem 1751: error = strerror(errno);
1.62 lukem 1752: perror_reply(550, dispname);
1.1 cgd 1753: goto done;
1754: }
1755: if (c == '\n')
1756: i++;
1.8 deraadt 1757: }
1.31 kleink 1758: } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1.138 lukem 1759: error = strerror(errno);
1.62 lukem 1760: perror_reply(550, dispname);
1.1 cgd 1761: goto done;
1762: }
1763: }
1.62 lukem 1764: dout = dataconn(dispname, st.st_size, "w");
1.1 cgd 1765: if (dout == NULL)
1766: goto done;
1.62 lukem 1767:
1.139 enami 1768: (void)getrusage(RUSAGE_SELF, &rusage_before);
1.62 lukem 1769: (void)gettimeofday(&start, NULL);
1.139 enami 1770: sendrv = send_data(fin, dout, &st, isdata);
1.62 lukem 1771: (void)gettimeofday(&finish, NULL);
1.139 enami 1772: (void)getrusage(RUSAGE_SELF, &rusage_after);
1.137 lukem 1773: closedataconn(dout); /* close now to affect timing stats */
1.62 lukem 1774: timersub(&finish, &start, &td);
1775: tdp = &td;
1.89 lukem 1776: done:
1.148 thorpej 1777: if (dolog) {
1.138 lukem 1778: logxfer("get", byte_count, name, NULL, tdp, error);
1.139 enami 1779: if (tdp != NULL)
1780: logrusage(&rusage_before, &rusage_after);
1781: }
1.49 lukem 1782: closerv = (*closefunc)(fin);
1783: if (sendrv == 0) {
1.132 lukem 1784: FILE *errf;
1.49 lukem 1785: struct stat sb;
1786:
1.71 lukem 1787: if (!isls && argv != NULL && closerv != 0) {
1.95 lukem 1788: reply(-226,
1.49 lukem 1789: "Command returned an exit status of %d",
1790: closerv);
1791: if (isconversion)
1.119 lukem 1792: syslog(LOG_WARNING,
1.71 lukem 1793: "retrieve command: '%s' returned %d",
1794: argv[0], closerv);
1.49 lukem 1795: }
1.71 lukem 1796: if (!isls && argv != NULL && stderrfd != -1 &&
1.49 lukem 1797: (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 &&
1.132 lukem 1798: ((errf = fdopen(stderrfd, "r")) != NULL)) {
1.49 lukem 1799: char *cp, line[LINE_MAX];
1800:
1.95 lukem 1801: reply(-226, "Command error messages:");
1.132 lukem 1802: rewind(errf);
1803: while (fgets(line, sizeof(line), errf) != NULL) {
1.49 lukem 1804: if ((cp = strchr(line, '\n')) != NULL)
1805: *cp = '\0';
1.95 lukem 1806: reply(0, " %s", line);
1.49 lukem 1807: }
1808: (void) fflush(stdout);
1.132 lukem 1809: (void) fclose(errf);
1.49 lukem 1810: /* a reply(226,) must follow */
1811: }
1812: reply(226, "Transfer complete.");
1813: }
1.71 lukem 1814: cleanupretrieve:
1.49 lukem 1815: if (stderrfd != -1)
1816: (void)close(stderrfd);
1.71 lukem 1817: if (isconversion)
1818: free(argv);
1.1 cgd 1819: }
1820:
1.8 deraadt 1821: void
1.132 lukem 1822: store(const char *name, const char *fmode, int unique)
1.1 cgd 1823: {
1824: FILE *fout, *din;
1825: struct stat st;
1.88 lukem 1826: int (*closefunc)(FILE *);
1.62 lukem 1827: struct timeval start, finish, td, *tdp;
1.193 lukem 1828: const char *desc, *error;
1.1 cgd 1829:
1.95 lukem 1830: din = NULL;
1.132 lukem 1831: desc = (*fmode == 'w') ? "put" : "append";
1.138 lukem 1832: error = NULL;
1.1 cgd 1833: if (unique && stat(name, &st) == 0 &&
1.8 deraadt 1834: (name = gunique(name)) == NULL) {
1.118 lukem 1835: logxfer(desc, -1, name, NULL, NULL,
1836: "cannot create unique file");
1.89 lukem 1837: goto cleanupstore;
1.8 deraadt 1838: }
1.1 cgd 1839:
1840: if (restart_point)
1.132 lukem 1841: fmode = "r+";
1842: fout = fopen(name, fmode);
1.1 cgd 1843: closefunc = fclose;
1.62 lukem 1844: tdp = NULL;
1.1 cgd 1845: if (fout == NULL) {
1846: perror_reply(553, name);
1.118 lukem 1847: logxfer(desc, -1, name, NULL, NULL, strerror(errno));
1.89 lukem 1848: goto cleanupstore;
1.1 cgd 1849: }
1.8 deraadt 1850: byte_count = -1;
1.1 cgd 1851: if (restart_point) {
1852: if (type == TYPE_A) {
1.49 lukem 1853: off_t i;
1.8 deraadt 1854: int c;
1.1 cgd 1855:
1.49 lukem 1856: for (i = 0; i < restart_point; i++) {
1.1 cgd 1857: if ((c=getc(fout)) == EOF) {
1.138 lukem 1858: error = strerror(errno);
1.1 cgd 1859: perror_reply(550, name);
1860: goto done;
1861: }
1862: if (c == '\n')
1863: i++;
1.8 deraadt 1864: }
1.1 cgd 1865: /*
1866: * We must do this seek to "current" position
1867: * because we are changing from reading to
1868: * writing.
1869: */
1.20 lukem 1870: if (fseek(fout, 0L, SEEK_CUR) < 0) {
1.138 lukem 1871: error = strerror(errno);
1.1 cgd 1872: perror_reply(550, name);
1873: goto done;
1874: }
1.31 kleink 1875: } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1.138 lukem 1876: error = strerror(errno);
1.1 cgd 1877: perror_reply(550, name);
1878: goto done;
1879: }
1880: }
1881: din = dataconn(name, (off_t)-1, "r");
1882: if (din == NULL)
1883: goto done;
1.62 lukem 1884: (void)gettimeofday(&start, NULL);
1.1 cgd 1885: if (receive_data(din, fout) == 0) {
1886: if (unique)
1887: reply(226, "Transfer complete (unique file name:%s).",
1888: name);
1889: else
1890: reply(226, "Transfer complete.");
1891: }
1.62 lukem 1892: (void)gettimeofday(&finish, NULL);
1.137 lukem 1893: closedataconn(din); /* close now to affect timing stats */
1.62 lukem 1894: timersub(&finish, &start, &td);
1895: tdp = &td;
1.89 lukem 1896: done:
1.138 lukem 1897: logxfer(desc, byte_count, name, NULL, tdp, error);
1.1 cgd 1898: (*closefunc)(fout);
1.89 lukem 1899: cleanupstore:
1.137 lukem 1900: ;
1.1 cgd 1901: }
1902:
1.8 deraadt 1903: static FILE *
1.132 lukem 1904: getdatasock(const char *fmode)
1.1 cgd 1905: {
1.101 lukem 1906: int on, s, t, tries;
1907: in_port_t port;
1.1 cgd 1908:
1.101 lukem 1909: on = 1;
1.1 cgd 1910: if (data >= 0)
1.132 lukem 1911: return (fdopen(data, fmode));
1.101 lukem 1912: if (! dropprivs)
1913: (void) seteuid((uid_t)0);
1.67 itojun 1914: s = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
1.1 cgd 1915: if (s < 0)
1916: goto bad;
1917: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1.8 deraadt 1918: (char *) &on, sizeof(on)) < 0)
1.1 cgd 1919: goto bad;
1.66 briggs 1920: if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1921: (char *) &on, sizeof(on)) < 0)
1922: goto bad;
1.101 lukem 1923: /* anchor socket to avoid multi-homing problems */
1.67 itojun 1924: data_source = ctrl_addr;
1.101 lukem 1925: /*
1926: * By default source port for PORT connctions is
1927: * ctrlport-1 (see RFC959 section 5.2).
1928: * However, if privs have been dropped and that
1929: * would be < IPPORT_RESERVED, use a random port
1930: * instead.
1931: */
1.111 lukem 1932: if (dataport)
1933: port = dataport;
1934: else
1935: port = ntohs(ctrl_addr.su_port) - 1;
1.101 lukem 1936: if (dropprivs && port < IPPORT_RESERVED)
1937: port = 0; /* use random port */
1938: data_source.su_port = htons(port);
1939:
1.1 cgd 1940: for (tries = 1; ; tries++) {
1.109 lukem 1941: if (bind(s, (struct sockaddr *)&data_source.si_su,
1.67 itojun 1942: data_source.su_len) >= 0)
1.1 cgd 1943: break;
1944: if (errno != EADDRINUSE || tries > 10)
1945: goto bad;
1946: sleep(tries);
1947: }
1.101 lukem 1948: if (! dropprivs)
1949: (void) seteuid((uid_t)pw->pw_uid);
1.1 cgd 1950: #ifdef IP_TOS
1.75 itojun 1951: if (!mapped && ctrl_addr.su_family == AF_INET) {
1.67 itojun 1952: on = IPTOS_THROUGHPUT;
1953: if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on,
1954: sizeof(int)) < 0)
1955: syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1956: }
1.1 cgd 1957: #endif
1.132 lukem 1958: return (fdopen(s, fmode));
1.89 lukem 1959: bad:
1.101 lukem 1960: /* Return the real value of errno (close may change it) */
1.8 deraadt 1961: t = errno;
1.101 lukem 1962: if (! dropprivs)
1963: (void) seteuid((uid_t)pw->pw_uid);
1.1 cgd 1964: (void) close(s);
1.8 deraadt 1965: errno = t;
1.1 cgd 1966: return (NULL);
1967: }
1968:
1.93 lukem 1969: FILE *
1.132 lukem 1970: dataconn(const char *name, off_t size, const char *fmode)
1.1 cgd 1971: {
1972: char sizebuf[32];
1973: FILE *file;
1.150 lukem 1974: int retry, tos, keepalive, conerrno;
1.1 cgd 1975:
1976: file_size = size;
1977: byte_count = 0;
1978: if (size != (off_t) -1)
1.109 lukem 1979: (void)snprintf(sizebuf, sizeof(sizebuf), " (" LLF " byte%s)",
1980: (LLT)size, PLURAL(size));
1.1 cgd 1981: else
1.29 mrg 1982: sizebuf[0] = '\0';
1.1 cgd 1983: if (pdata >= 0) {
1.109 lukem 1984: struct sockinet from;
1.176 mrg 1985: int s;
1986: socklen_t fromlen = sizeof(from.su_len);
1.1 cgd 1987:
1.42 lukem 1988: (void) alarm(curclass.timeout);
1.109 lukem 1989: s = accept(pdata, (struct sockaddr *)&from.si_su, &fromlen);
1.42 lukem 1990: (void) alarm(0);
1.1 cgd 1991: if (s < 0) {
1992: reply(425, "Can't open data connection.");
1993: (void) close(pdata);
1994: pdata = -1;
1.8 deraadt 1995: return (NULL);
1.1 cgd 1996: }
1997: (void) close(pdata);
1998: pdata = s;
1.67 itojun 1999: switch (from.su_family) {
2000: case AF_INET:
1.1 cgd 2001: #ifdef IP_TOS
1.75 itojun 2002: if (!mapped) {
2003: tos = IPTOS_THROUGHPUT;
2004: (void) setsockopt(s, IPPROTO_IP, IP_TOS,
2005: (char *)&tos, sizeof(int));
2006: }
1.67 itojun 2007: break;
1.66 briggs 2008: #endif
1.67 itojun 2009: }
1.66 briggs 2010: /* Set keepalives on the socket to detect dropped conns. */
2011: #ifdef SO_KEEPALIVE
2012: keepalive = 1;
2013: (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
2014: (char *)&keepalive, sizeof(int));
1.1 cgd 2015: #endif
1.8 deraadt 2016: reply(150, "Opening %s mode data connection for '%s'%s.",
1.1 cgd 2017: type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1.132 lukem 2018: return (fdopen(pdata, fmode));
1.1 cgd 2019: }
2020: if (data >= 0) {
1.8 deraadt 2021: reply(125, "Using existing data connection for '%s'%s.",
1.1 cgd 2022: name, sizebuf);
2023: usedefault = 1;
1.132 lukem 2024: return (fdopen(data, fmode));
1.1 cgd 2025: }
2026: if (usedefault)
2027: data_dest = his_addr;
2028: usedefault = 1;
1.150 lukem 2029: retry = conerrno = 0;
2030: do {
2031: file = getdatasock(fmode);
2032: if (file == NULL) {
2033: char hbuf[NI_MAXHOST];
2034: char pbuf[NI_MAXSERV];
2035:
2036: if (getnameinfo((struct sockaddr *)&data_source.si_su,
2037: data_source.su_len, hbuf, sizeof(hbuf), pbuf,
2038: sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV))
2039: strlcpy(hbuf, "?", sizeof(hbuf));
2040: reply(425, "Can't create data socket (%s,%s): %s.",
2041: hbuf, pbuf, strerror(errno));
2042: return (NULL);
2043: }
2044: data = fileno(file);
2045: conerrno = 0;
2046: if (connect(data, (struct sockaddr *)&data_dest.si_su,
2047: data_dest.su_len) == 0)
2048: break;
2049: conerrno = errno;
2050: (void) fclose(file);
1.174 peter 2051: file = NULL;
1.150 lukem 2052: data = -1;
2053: if (conerrno == EADDRINUSE) {
1.1 cgd 2054: sleep((unsigned) swaitint);
2055: retry += swaitint;
1.150 lukem 2056: } else {
2057: break;
1.1 cgd 2058: }
1.150 lukem 2059: } while (retry <= swaitmax);
2060: if (conerrno != 0) {
1.1 cgd 2061: perror_reply(425, "Can't build data connection");
2062: return (NULL);
2063: }
1.8 deraadt 2064: reply(150, "Opening %s mode data connection for '%s'%s.",
1.1 cgd 2065: type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
2066: return (file);
2067: }
2068:
1.95 lukem 2069: void
2070: closedataconn(FILE *fd)
2071: {
2072:
1.136 lukem 2073: if (fd == NULL)
2074: return;
2075: (void)fclose(fd);
1.95 lukem 2076: data = -1;
2077: if (pdata >= 0)
2078: (void)close(pdata);
2079: pdata = -1;
2080: }
2081:
1.139 enami 2082: int
2083: write_data(int fd, char *buf, size_t size, off_t *bufrem,
2084: struct timeval *then, int isdata)
2085: {
2086: struct timeval now, td;
2087: ssize_t c;
2088:
2089: while (size > 0) {
2090: c = size;
2091: if (curclass.writesize) {
2092: if (curclass.writesize < c)
2093: c = curclass.writesize;
2094: }
2095: if (curclass.rateget) {
2096: if (*bufrem < c)
2097: c = *bufrem;
2098: }
2099: (void) alarm(curclass.timeout);
2100: c = write(fd, buf, c);
2101: if (c <= 0)
2102: return (1);
2103: buf += c;
2104: size -= c;
2105: byte_count += c;
2106: if (isdata) {
2107: total_data_out += c;
2108: total_data += c;
2109: }
2110: total_bytes_out += c;
2111: total_bytes += c;
2112: if (curclass.rateget) {
2113: *bufrem -= c;
2114: if (*bufrem == 0) {
2115: (void)gettimeofday(&now, NULL);
2116: timersub(&now, then, &td);
2117: if (td.tv_sec == 0) {
2118: usleep(1000000 - td.tv_usec);
2119: (void)gettimeofday(then, NULL);
2120: } else
2121: *then = now;
2122: *bufrem = curclass.rateget;
2123: }
2124: }
2125: }
2126: return (0);
2127: }
2128:
2129: static enum send_status
2130: send_data_with_read(int filefd, int netfd, const struct stat *st, int isdata)
2131: {
2132: struct timeval then;
2133: off_t bufrem;
1.193 lukem 2134: ssize_t readsize;
1.139 enami 2135: char *buf;
2136: int c, error;
2137:
1.193 lukem 2138: if (curclass.readsize > 0)
1.139 enami 2139: readsize = curclass.readsize;
2140: else
1.193 lukem 2141: readsize = st->st_blksize;
1.139 enami 2142: if ((buf = malloc(readsize)) == NULL) {
2143: perror_reply(451, "Local resource failure: malloc");
2144: return (SS_NO_TRANSFER);
2145: }
2146:
2147: if (curclass.rateget) {
2148: bufrem = curclass.rateget;
2149: (void)gettimeofday(&then, NULL);
1.193 lukem 2150: } else
2151: bufrem = readsize;
1.139 enami 2152: while (1) {
2153: (void) alarm(curclass.timeout);
2154: c = read(filefd, buf, readsize);
2155: if (c == 0)
2156: error = SS_SUCCESS;
2157: else if (c < 0)
2158: error = SS_FILE_ERROR;
2159: else if (write_data(netfd, buf, c, &bufrem, &then, isdata))
2160: error = SS_DATA_ERROR;
1.158 lukem 2161: else if (urgflag && handleoobcmd())
2162: error = SS_ABORTED;
1.139 enami 2163: else
2164: continue;
2165:
2166: free(buf);
2167: return (error);
2168: }
2169: }
2170:
2171: static enum send_status
2172: send_data_with_mmap(int filefd, int netfd, const struct stat *st, int isdata)
2173: {
2174: struct timeval then;
2175: off_t bufrem, filesize, off, origoff;
1.193 lukem 2176: ssize_t mapsize, winsize;
1.139 enami 2177: int error, sendbufsize, sendlowat;
2178: void *win;
2179:
1.193 lukem 2180: bufrem = 0;
1.139 enami 2181: if (curclass.sendbufsize) {
2182: sendbufsize = curclass.sendbufsize;
2183: if (setsockopt(netfd, SOL_SOCKET, SO_SNDBUF,
2184: &sendbufsize, sizeof(int)) == -1)
2185: syslog(LOG_WARNING, "setsockopt(SO_SNDBUF, %d): %m",
2186: sendbufsize);
2187: }
2188:
2189: if (curclass.sendlowat) {
2190: sendlowat = curclass.sendlowat;
2191: if (setsockopt(netfd, SOL_SOCKET, SO_SNDLOWAT,
2192: &sendlowat, sizeof(int)) == -1)
2193: syslog(LOG_WARNING, "setsockopt(SO_SNDLOWAT, %d): %m",
2194: sendlowat);
2195: }
2196:
2197: winsize = curclass.mmapsize;
2198: filesize = st->st_size;
1.171 christos 2199: if (ftpd_debug)
1.193 lukem 2200: syslog(LOG_INFO, "mmapsize = " LLF ", writesize = " LLF,
2201: (LLT)winsize, (LLT)curclass.writesize);
2202: if (winsize <= 0)
1.139 enami 2203: goto try_read;
2204:
2205: off = lseek(filefd, (off_t)0, SEEK_CUR);
2206: if (off == -1)
2207: goto try_read;
2208:
2209: origoff = off;
2210: if (curclass.rateget) {
2211: bufrem = curclass.rateget;
2212: (void)gettimeofday(&then, NULL);
1.193 lukem 2213: } else
2214: bufrem = winsize;
1.139 enami 2215: while (1) {
2216: mapsize = MIN(filesize - off, winsize);
2217: if (mapsize == 0)
2218: break;
2219: win = mmap(NULL, mapsize, PROT_READ,
2220: MAP_FILE|MAP_SHARED, filefd, off);
2221: if (win == MAP_FAILED) {
2222: if (off == origoff)
2223: goto try_read;
2224: return (SS_FILE_ERROR);
2225: }
2226: (void) madvise(win, mapsize, MADV_SEQUENTIAL);
2227: error = write_data(netfd, win, mapsize, &bufrem, &then,
2228: isdata);
2229: (void) madvise(win, mapsize, MADV_DONTNEED);
2230: munmap(win, mapsize);
1.158 lukem 2231: if (urgflag && handleoobcmd())
2232: return (SS_ABORTED);
1.139 enami 2233: if (error)
2234: return (SS_DATA_ERROR);
2235: off += mapsize;
2236: }
2237: return (SS_SUCCESS);
2238:
2239: try_read:
2240: return (send_data_with_read(filefd, netfd, st, isdata));
2241: }
2242:
1.1 cgd 2243: /*
1.183 lukem 2244: * Transfer the contents of "instr" to "outstr" peer using the appropriate
1.139 enami 2245: * encapsulation of the data subject to Mode, Structure, and Type.
1.1 cgd 2246: *
2247: * NB: Form isn't handled.
2248: */
1.49 lukem 2249: static int
1.139 enami 2250: send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
1.1 cgd 2251: {
1.73 lukem 2252: int c, filefd, netfd, rval;
1.1 cgd 2253:
1.158 lukem 2254: urgflag = 0;
1.62 lukem 2255: transflag = 1;
2256: rval = -1;
1.24 lukem 2257:
1.1 cgd 2258: switch (type) {
2259:
2260: case TYPE_A:
1.109 lukem 2261: /* XXXLUKEM: rate limit ascii send (get) */
1.62 lukem 2262: (void) alarm(curclass.timeout);
1.1 cgd 2263: while ((c = getc(instr)) != EOF) {
1.158 lukem 2264: if (urgflag && handleoobcmd())
2265: goto cleanup_send_data;
1.1 cgd 2266: byte_count++;
1.63 lukem 2267: if (c == '\n') {
2268: if (ferror(outstr))
2269: goto data_err;
2270: (void) putc('\r', outstr);
2271: if (isdata) {
2272: total_data_out++;
2273: total_data++;
2274: }
2275: total_bytes_out++;
2276: total_bytes++;
2277: }
2278: (void) putc(c, outstr);
1.62 lukem 2279: if (isdata) {
2280: total_data_out++;
2281: total_data++;
2282: }
2283: total_bytes_out++;
2284: total_bytes++;
2285: if ((byte_count % 4096) == 0)
2286: (void) alarm(curclass.timeout);
1.1 cgd 2287: }
1.62 lukem 2288: (void) alarm(0);
1.1 cgd 2289: fflush(outstr);
2290: if (ferror(instr))
2291: goto file_err;
2292: if (ferror(outstr))
2293: goto data_err;
1.62 lukem 2294: rval = 0;
2295: goto cleanup_send_data;
1.1 cgd 2296:
2297: case TYPE_I:
2298: case TYPE_L:
1.63 lukem 2299: filefd = fileno(instr);
1.1 cgd 2300: netfd = fileno(outstr);
1.139 enami 2301: switch (send_data_with_mmap(filefd, netfd, st, isdata)) {
2302:
2303: case SS_SUCCESS:
2304: break;
2305:
1.158 lukem 2306: case SS_ABORTED:
1.139 enami 2307: case SS_NO_TRANSFER:
2308: goto cleanup_send_data;
2309:
2310: case SS_FILE_ERROR:
2311: goto file_err;
1.73 lukem 2312:
1.139 enami 2313: case SS_DATA_ERROR:
2314: goto data_err;
1.1 cgd 2315: }
1.62 lukem 2316: rval = 0;
2317: goto cleanup_send_data;
2318:
1.1 cgd 2319: default:
2320: reply(550, "Unimplemented TYPE %d in send_data", type);
1.62 lukem 2321: goto cleanup_send_data;
1.1 cgd 2322: }
2323:
1.89 lukem 2324: data_err:
1.62 lukem 2325: (void) alarm(0);
1.1 cgd 2326: perror_reply(426, "Data connection");
1.62 lukem 2327: goto cleanup_send_data;
1.1 cgd 2328:
1.89 lukem 2329: file_err:
1.62 lukem 2330: (void) alarm(0);
2331: perror_reply(551, "Error on input file");
1.158 lukem 2332: goto cleanup_send_data;
1.73 lukem 2333:
1.89 lukem 2334: cleanup_send_data:
1.62 lukem 2335: (void) alarm(0);
1.1 cgd 2336: transflag = 0;
1.158 lukem 2337: urgflag = 0;
1.62 lukem 2338: if (isdata) {
2339: total_files_out++;
2340: total_files++;
2341: }
2342: total_xfers_out++;
2343: total_xfers++;
2344: return (rval);
1.1 cgd 2345: }
2346:
2347: /*
1.8 deraadt 2348: * Transfer data from peer to "outstr" using the appropriate encapulation of
2349: * the data subject to Mode, Structure, and Type.
1.1 cgd 2350: *
2351: * N.B.: Form isn't handled.
2352: */
1.8 deraadt 2353: static int
1.88 lukem 2354: receive_data(FILE *instr, FILE *outstr)
1.1 cgd 2355: {
1.178 christos 2356: int c, netfd, filefd, rval;
2357: int volatile bare_lfs;
1.111 lukem 2358: off_t byteswritten;
1.168 ginsbach 2359: char *buf;
1.193 lukem 2360: ssize_t readsize;
1.158 lukem 2361: struct sigaction sa, sa_saved;
1.168 ginsbach 2362: struct stat st;
1.1 cgd 2363:
1.158 lukem 2364: memset(&sa, 0, sizeof(sa));
2365: sigfillset(&sa.sa_mask);
2366: sa.sa_flags = SA_RESTART;
2367: sa.sa_handler = lostconn;
2368: (void) sigaction(SIGALRM, &sa, &sa_saved);
2369:
1.24 lukem 2370: bare_lfs = 0;
1.158 lukem 2371: urgflag = 0;
1.62 lukem 2372: transflag = 1;
2373: rval = -1;
1.111 lukem 2374: byteswritten = 0;
1.168 ginsbach 2375: buf = NULL;
1.24 lukem 2376:
1.111 lukem 2377: #define FILESIZECHECK(x) \
2378: do { \
2379: if (curclass.maxfilesize != -1 && \
2380: (x) > curclass.maxfilesize) { \
2381: errno = EFBIG; \
2382: goto file_err; \
2383: } \
2384: } while (0)
2385:
1.1 cgd 2386: switch (type) {
2387:
2388: case TYPE_I:
2389: case TYPE_L:
1.62 lukem 2390: netfd = fileno(instr);
2391: filefd = fileno(outstr);
2392: (void) alarm(curclass.timeout);
1.168 ginsbach 2393: if (curclass.readsize)
2394: readsize = curclass.readsize;
2395: else if (fstat(filefd, &st))
1.193 lukem 2396: readsize = (ssize_t)st.st_blksize;
1.168 ginsbach 2397: else
2398: readsize = BUFSIZ;
2399: if ((buf = malloc(readsize)) == NULL) {
2400: perror_reply(451, "Local resource failure: malloc");
2401: goto cleanup_recv_data;
2402: }
1.73 lukem 2403: if (curclass.rateput) {
2404: while (1) {
2405: int d;
2406: struct timeval then, now, td;
2407: off_t bufrem;
2408:
2409: (void)gettimeofday(&then, NULL);
2410: errno = c = d = 0;
2411: for (bufrem = curclass.rateput; bufrem > 0; ) {
2412: if ((c = read(netfd, buf,
1.168 ginsbach 2413: MIN(readsize, bufrem))) <= 0)
1.73 lukem 2414: goto recvdone;
1.158 lukem 2415: if (urgflag && handleoobcmd())
2416: goto cleanup_recv_data;
1.111 lukem 2417: FILESIZECHECK(byte_count + c);
1.73 lukem 2418: if ((d = write(filefd, buf, c)) != c)
1.111 lukem 2419: goto file_err;
1.73 lukem 2420: (void) alarm(curclass.timeout);
2421: bufrem -= c;
2422: byte_count += c;
2423: total_data_in += c;
2424: total_data += c;
2425: total_bytes_in += c;
2426: total_bytes += c;
2427: }
2428: (void)gettimeofday(&now, NULL);
2429: timersub(&now, &then, &td);
1.96 lukem 2430: if (td.tv_sec == 0)
2431: usleep(1000000 - td.tv_usec);
1.73 lukem 2432: }
2433: } else {
1.168 ginsbach 2434: while ((c = read(netfd, buf, readsize)) > 0) {
1.158 lukem 2435: if (urgflag && handleoobcmd())
2436: goto cleanup_recv_data;
1.111 lukem 2437: FILESIZECHECK(byte_count + c);
1.73 lukem 2438: if (write(filefd, buf, c) != c)
2439: goto file_err;
2440: (void) alarm(curclass.timeout);
2441: byte_count += c;
2442: total_data_in += c;
2443: total_data += c;
2444: total_bytes_in += c;
2445: total_bytes += c;
2446: }
1.1 cgd 2447: }
1.73 lukem 2448: recvdone:
2449: if (c < 0)
1.1 cgd 2450: goto data_err;
1.62 lukem 2451: rval = 0;
2452: goto cleanup_recv_data;
1.1 cgd 2453:
2454: case TYPE_E:
2455: reply(553, "TYPE E not implemented.");
1.62 lukem 2456: goto cleanup_recv_data;
1.1 cgd 2457:
2458: case TYPE_A:
1.62 lukem 2459: (void) alarm(curclass.timeout);
1.109 lukem 2460: /* XXXLUKEM: rate limit ascii receive (put) */
1.1 cgd 2461: while ((c = getc(instr)) != EOF) {
1.158 lukem 2462: if (urgflag && handleoobcmd())
2463: goto cleanup_recv_data;
1.1 cgd 2464: byte_count++;
1.62 lukem 2465: total_data_in++;
2466: total_data++;
2467: total_bytes_in++;
2468: total_bytes++;
2469: if ((byte_count % 4096) == 0)
2470: (void) alarm(curclass.timeout);
1.1 cgd 2471: if (c == '\n')
2472: bare_lfs++;
2473: while (c == '\r') {
2474: if (ferror(outstr))
2475: goto data_err;
2476: if ((c = getc(instr)) != '\n') {
1.62 lukem 2477: byte_count++;
2478: total_data_in++;
2479: total_data++;
2480: total_bytes_in++;
2481: total_bytes++;
2482: if ((byte_count % 4096) == 0)
2483: (void) alarm(curclass.timeout);
1.111 lukem 2484: byteswritten++;
2485: FILESIZECHECK(byteswritten);
1.1 cgd 2486: (void) putc ('\r', outstr);
2487: if (c == '\0' || c == EOF)
2488: goto contin2;
2489: }
2490: }
1.111 lukem 2491: byteswritten++;
2492: FILESIZECHECK(byteswritten);
1.1 cgd 2493: (void) putc(c, outstr);
1.111 lukem 2494: contin2: ;
1.1 cgd 2495: }
1.62 lukem 2496: (void) alarm(0);
1.1 cgd 2497: fflush(outstr);
2498: if (ferror(instr))
2499: goto data_err;
2500: if (ferror(outstr))
2501: goto file_err;
2502: if (bare_lfs) {
1.95 lukem 2503: reply(-226,
1.62 lukem 2504: "WARNING! %d bare linefeeds received in ASCII mode",
1.8 deraadt 2505: bare_lfs);
1.95 lukem 2506: reply(0, "File may not have transferred correctly.");
1.1 cgd 2507: }
1.62 lukem 2508: rval = 0;
2509: goto cleanup_recv_data;
2510:
1.1 cgd 2511: default:
2512: reply(550, "Unimplemented TYPE %d in receive_data", type);
1.62 lukem 2513: goto cleanup_recv_data;
1.1 cgd 2514: }
1.120 cgd 2515: #undef FILESIZECHECK
1.1 cgd 2516:
1.89 lukem 2517: data_err:
1.62 lukem 2518: (void) alarm(0);
1.1 cgd 2519: perror_reply(426, "Data Connection");
1.62 lukem 2520: goto cleanup_recv_data;
1.1 cgd 2521:
1.89 lukem 2522: file_err:
1.62 lukem 2523: (void) alarm(0);
2524: perror_reply(452, "Error writing file");
2525: goto cleanup_recv_data;
2526:
1.89 lukem 2527: cleanup_recv_data:
1.62 lukem 2528: (void) alarm(0);
1.158 lukem 2529: (void) sigaction(SIGALRM, &sa_saved, NULL);
1.168 ginsbach 2530: if (buf)
2531: free(buf);
1.1 cgd 2532: transflag = 0;
1.158 lukem 2533: urgflag = 0;
1.62 lukem 2534: total_files_in++;
2535: total_files++;
2536: total_xfers_in++;
2537: total_xfers++;
2538: return (rval);
1.1 cgd 2539: }
2540:
1.8 deraadt 2541: void
1.88 lukem 2542: statcmd(void)
1.1 cgd 2543: {
1.109 lukem 2544: struct sockinet *su = NULL;
1.112 itojun 2545: static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
1.185 lukem 2546: unsigned char *a, *p;
1.89 lukem 2547: int ispassive, af;
1.93 lukem 2548: off_t otbi, otbo, otb;
1.1 cgd 2549:
1.197 plunky 2550: a = p = NULL;
1.67 itojun 2551:
1.95 lukem 2552: reply(-211, "%s FTP server status:", hostname);
1.101 lukem 2553: reply(0, "Version: %s", EMPTYSTR(version) ? "<suppressed>" : version);
1.112 itojun 2554: hbuf[0] = '\0';
1.109 lukem 2555: if (!getnameinfo((struct sockaddr *)&his_addr.si_su, his_addr.su_len,
1.112 itojun 2556: hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)
2557: && strcmp(remotehost, hbuf) != 0)
2558: reply(0, "Connected to %s (%s)", remotehost, hbuf);
1.104 christos 2559: else
1.95 lukem 2560: reply(0, "Connected to %s", remotehost);
1.104 christos 2561:
1.1 cgd 2562: if (logged_in) {
1.73 lukem 2563: if (curclass.type == CLASS_GUEST)
1.95 lukem 2564: reply(0, "Logged in anonymously");
1.1 cgd 2565: else
1.95 lukem 2566: reply(0, "Logged in as %s%s", pw->pw_name,
1.73 lukem 2567: curclass.type == CLASS_CHROOT ? " (chroot)" : "");
1.1 cgd 2568: } else if (askpasswd)
1.95 lukem 2569: reply(0, "Waiting for password");
1.1 cgd 2570: else
1.95 lukem 2571: reply(0, "Waiting for user name");
2572: cprintf(stdout, " TYPE: %s", typenames[type]);
1.93 lukem 2573: if (type == TYPE_A || type == TYPE_E)
1.95 lukem 2574: cprintf(stdout, ", FORM: %s", formnames[form]);
1.62 lukem 2575: if (type == TYPE_L) {
1.1 cgd 2576: #if NBBY == 8
1.95 lukem 2577: cprintf(stdout, " %d", NBBY);
1.1 cgd 2578: #else
1.62 lukem 2579: /* XXX: `bytesize' needs to be defined in this case */
1.95 lukem 2580: cprintf(stdout, " %d", bytesize);
1.1 cgd 2581: #endif
1.62 lukem 2582: }
1.95 lukem 2583: cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
1.1 cgd 2584: strunames[stru], modenames[mode]);
1.67 itojun 2585: ispassive = 0;
2586: if (data != -1) {
1.147 lukem 2587: reply(0, "Data connection open");
1.67 itojun 2588: su = NULL;
2589: } else if (pdata != -1) {
1.95 lukem 2590: reply(0, "in Passive mode");
1.118 lukem 2591: if (curclass.advertise.su_len != 0)
2592: su = &curclass.advertise;
2593: else
2594: su = &pasv_addr;
1.67 itojun 2595: ispassive = 1;
1.1 cgd 2596: goto printaddr;
2597: } else if (usedefault == 0) {
1.173 peter 2598: su = (struct sockinet *)&data_dest;
2599:
1.67 itojun 2600: if (epsvall) {
1.95 lukem 2601: reply(0, "EPSV only mode (EPSV ALL)");
1.67 itojun 2602: goto epsvonly;
2603: }
1.89 lukem 2604: printaddr:
1.93 lukem 2605: /* PASV/PORT */
1.67 itojun 2606: if (su->su_family == AF_INET) {
1.185 lukem 2607: a = (unsigned char *) &su->su_addr;
2608: p = (unsigned char *) &su->su_port;
1.1 cgd 2609: #define UC(b) (((int) b) & 0xff)
1.95 lukem 2610: reply(0, "%s (%d,%d,%d,%d,%d,%d)",
1.93 lukem 2611: ispassive ? "PASV" : "PORT" ,
1.67 itojun 2612: UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2613: UC(p[0]), UC(p[1]));
2614: }
2615:
1.93 lukem 2616: /* LPSV/LPRT */
1.67 itojun 2617: {
1.132 lukem 2618: int alen, i;
1.67 itojun 2619:
2620: alen = 0;
2621: switch (su->su_family) {
2622: case AF_INET:
1.185 lukem 2623: a = (unsigned char *) &su->su_addr;
2624: p = (unsigned char *) &su->su_port;
1.109 lukem 2625: alen = sizeof(su->su_addr);
1.67 itojun 2626: af = 4;
2627: break;
1.104 christos 2628: #ifdef INET6
1.67 itojun 2629: case AF_INET6:
1.185 lukem 2630: a = (unsigned char *) &su->su_6addr;
2631: p = (unsigned char *) &su->su_port;
1.109 lukem 2632: alen = sizeof(su->su_6addr);
1.67 itojun 2633: af = 6;
2634: break;
1.104 christos 2635: #endif
1.67 itojun 2636: default:
2637: af = 0;
2638: break;
2639: }
2640: if (af) {
1.95 lukem 2641: cprintf(stdout, " %s (%d,%d",
1.93 lukem 2642: ispassive ? "LPSV" : "LPRT", af, alen);
1.67 itojun 2643: for (i = 0; i < alen; i++)
1.95 lukem 2644: cprintf(stdout, ",%d", UC(a[i]));
1.112 itojun 2645: cprintf(stdout, ",%d,%d,%d)\r\n",
2646: 2, UC(p[0]), UC(p[1]));
1.1 cgd 2647: #undef UC
1.67 itojun 2648: }
2649: }
2650:
2651: /* EPRT/EPSV */
1.89 lukem 2652: epsvonly:
1.109 lukem 2653: af = af2epsvproto(su->su_family);
1.112 itojun 2654: hbuf[0] = '\0';
1.109 lukem 2655: if (af > 0) {
1.112 itojun 2656: struct sockinet tmp;
2657:
2658: tmp = *su;
1.113 lukem 2659: #ifdef INET6
1.112 itojun 2660: if (tmp.su_family == AF_INET6)
2661: tmp.su_scope_id = 0;
1.113 lukem 2662: #endif
1.112 itojun 2663: if (getnameinfo((struct sockaddr *)&tmp.si_su,
2664: tmp.su_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
2665: NI_NUMERICHOST | NI_NUMERICSERV) == 0)
2666: reply(0, "%s (|%d|%s|%s|)",
1.93 lukem 2667: ispassive ? "EPSV" : "EPRT",
1.112 itojun 2668: af, hbuf, sbuf);
1.67 itojun 2669: }
1.1 cgd 2670: } else
1.95 lukem 2671: reply(0, "No data connection");
1.62 lukem 2672:
2673: if (logged_in) {
1.109 lukem 2674: reply(0,
2675: "Data sent: " LLF " byte%s in " LLF " file%s",
2676: (LLT)total_data_out, PLURAL(total_data_out),
2677: (LLT)total_files_out, PLURAL(total_files_out));
2678: reply(0,
2679: "Data received: " LLF " byte%s in " LLF " file%s",
2680: (LLT)total_data_in, PLURAL(total_data_in),
2681: (LLT)total_files_in, PLURAL(total_files_in));
2682: reply(0,
2683: "Total data: " LLF " byte%s in " LLF " file%s",
2684: (LLT)total_data, PLURAL(total_data),
2685: (LLT)total_files, PLURAL(total_files));
1.62 lukem 2686: }
2687: otbi = total_bytes_in;
2688: otbo = total_bytes_out;
2689: otb = total_bytes;
1.109 lukem 2690: reply(0, "Traffic sent: " LLF " byte%s in " LLF " transfer%s",
2691: (LLT)otbo, PLURAL(otbo),
2692: (LLT)total_xfers_out, PLURAL(total_xfers_out));
2693: reply(0, "Traffic received: " LLF " byte%s in " LLF " transfer%s",
2694: (LLT)otbi, PLURAL(otbi),
2695: (LLT)total_xfers_in, PLURAL(total_xfers_in));
2696: reply(0, "Total traffic: " LLF " byte%s in " LLF " transfer%s",
2697: (LLT)otb, PLURAL(otb),
2698: (LLT)total_xfers, PLURAL(total_xfers));
1.24 lukem 2699:
1.133 lukem 2700: if (logged_in && !CURCLASS_FLAGS_ISSET(private)) {
1.24 lukem 2701: struct ftpconv *cp;
2702:
1.98 sommerfe 2703: reply(0, "%s", "");
1.95 lukem 2704: reply(0, "Class: %s, type: %s",
1.81 lukem 2705: curclass.classname, CURCLASSTYPE);
1.95 lukem 2706: reply(0, "Check PORT/LPRT commands: %sabled",
1.111 lukem 2707: CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
1.100 lukem 2708: if (! EMPTYSTR(curclass.display))
1.95 lukem 2709: reply(0, "Display file: %s", curclass.display);
1.100 lukem 2710: if (! EMPTYSTR(curclass.notify))
1.95 lukem 2711: reply(0, "Notify fileglob: %s", curclass.notify);
1.147 lukem 2712: reply(0, "Idle timeout: " LLF ", maximum timeout: " LLF,
2713: (LLT)curclass.timeout, (LLT)curclass.maxtimeout);
1.95 lukem 2714: reply(0, "Current connections: %d", connections);
1.82 lukem 2715: if (curclass.limit == -1)
1.95 lukem 2716: reply(0, "Maximum connections: unlimited");
1.82 lukem 2717: else
1.147 lukem 2718: reply(0, "Maximum connections: " LLF,
2719: (LLT)curclass.limit);
1.82 lukem 2720: if (curclass.limitfile)
1.111 lukem 2721: reply(0, "Connection limit exceeded message file: %s",
1.131 lukem 2722: conffilename(curclass.limitfile));
1.100 lukem 2723: if (! EMPTYSTR(curclass.chroot))
2724: reply(0, "Chroot format: %s", curclass.chroot);
1.135 lukem 2725: reply(0, "Deny bad ftpusers(5) quickly: %sabled",
1.133 lukem 2726: CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis");
1.100 lukem 2727: if (! EMPTYSTR(curclass.homedir))
2728: reply(0, "Homedir format: %s", curclass.homedir);
1.111 lukem 2729: if (curclass.maxfilesize == -1)
2730: reply(0, "Maximum file size: unlimited");
2731: else
2732: reply(0, "Maximum file size: " LLF,
2733: (LLT)curclass.maxfilesize);
1.100 lukem 2734: if (! EMPTYSTR(curclass.motd))
1.131 lukem 2735: reply(0, "MotD file: %s", conffilename(curclass.motd));
1.95 lukem 2736: reply(0,
1.73 lukem 2737: "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
1.111 lukem 2738: CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis");
1.95 lukem 2739: reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
1.111 lukem 2740: CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis");
2741: reply(0, "Sanitize file names: %sabled",
2742: CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis");
2743: reply(0, "PASV/LPSV/EPSV connections: %sabled",
2744: CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis");
1.118 lukem 2745: if (curclass.advertise.su_len != 0) {
2746: char buf[50]; /* big enough for IPv6 address */
2747: const char *bp;
2748:
2749: bp = inet_ntop(curclass.advertise.su_family,
2750: (void *)&curclass.advertise.su_addr,
2751: buf, sizeof(buf));
2752: if (bp != NULL)
1.128 lukem 2753: reply(0, "PASV advertise address: %s", bp);
1.118 lukem 2754: }
1.85 lukem 2755: if (curclass.portmin && curclass.portmax)
1.147 lukem 2756: reply(0, "PASV port range: " LLF " - " LLF,
2757: (LLT)curclass.portmin, (LLT)curclass.portmax);
1.73 lukem 2758: if (curclass.rateget)
1.111 lukem 2759: reply(0, "Rate get limit: " LLF " bytes/sec",
2760: (LLT)curclass.rateget);
1.73 lukem 2761: else
1.95 lukem 2762: reply(0, "Rate get limit: disabled");
1.73 lukem 2763: if (curclass.rateput)
1.111 lukem 2764: reply(0, "Rate put limit: " LLF " bytes/sec",
2765: (LLT)curclass.rateput);
1.73 lukem 2766: else
1.95 lukem 2767: reply(0, "Rate put limit: disabled");
1.147 lukem 2768: if (curclass.mmapsize)
2769: reply(0, "Mmap size: " LLF, (LLT)curclass.mmapsize);
2770: else
2771: reply(0, "Mmap size: disabled");
2772: if (curclass.readsize)
2773: reply(0, "Read size: " LLF, (LLT)curclass.readsize);
2774: else
2775: reply(0, "Read size: default");
2776: if (curclass.writesize)
2777: reply(0, "Write size: " LLF, (LLT)curclass.writesize);
2778: else
2779: reply(0, "Write size: default");
1.168 ginsbach 2780: if (curclass.recvbufsize)
2781: reply(0, "Receive buffer size: " LLF,
2782: (LLT)curclass.recvbufsize);
2783: else
2784: reply(0, "Receive buffer size: default");
1.147 lukem 2785: if (curclass.sendbufsize)
2786: reply(0, "Send buffer size: " LLF,
2787: (LLT)curclass.sendbufsize);
2788: else
2789: reply(0, "Send buffer size: default");
2790: if (curclass.sendlowat)
2791: reply(0, "Send low water mark: " LLF,
2792: (LLT)curclass.sendlowat);
2793: else
2794: reply(0, "Send low water mark: default");
1.95 lukem 2795: reply(0, "Umask: %.04o", curclass.umask);
1.24 lukem 2796: for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
2797: if (cp->suffix == NULL || cp->types == NULL ||
2798: cp->command == NULL)
2799: continue;
1.95 lukem 2800: reply(0, "Conversion: %s [%s] disable: %s, command: %s",
1.24 lukem 2801: cp->suffix, cp->types, cp->disable, cp->command);
2802: }
2803: }
2804:
1.1 cgd 2805: reply(211, "End of status");
2806: }
2807:
1.8 deraadt 2808: void
1.88 lukem 2809: fatal(const char *s)
1.1 cgd 2810: {
1.8 deraadt 2811:
1.1 cgd 2812: reply(451, "Error in server: %s\n", s);
2813: reply(221, "Closing connection due to server error.");
2814: dologout(0);
2815: /* NOTREACHED */
2816: }
2817:
1.93 lukem 2818: /*
2819: * reply() --
1.95 lukem 2820: * depending on the value of n, display fmt with a trailing CRLF and
2821: * prefix of:
2822: * n < -1 prefix the message with abs(n) + "-" (initial line)
2823: * n == 0 prefix the message with 4 spaces (middle lines)
2824: * n > 0 prefix the message with n + " " (final line)
1.93 lukem 2825: */
1.8 deraadt 2826: void
2827: reply(int n, const char *fmt, ...)
1.1 cgd 2828: {
1.158 lukem 2829: char msg[MAXPATHLEN * 2 + 100];
2830: size_t b;
2831: va_list ap;
1.88 lukem 2832:
1.62 lukem 2833: b = 0;
1.95 lukem 2834: if (n == 0)
1.158 lukem 2835: b = snprintf(msg, sizeof(msg), " ");
1.95 lukem 2836: else if (n < 0)
1.158 lukem 2837: b = snprintf(msg, sizeof(msg), "%d-", -n);
1.95 lukem 2838: else
1.158 lukem 2839: b = snprintf(msg, sizeof(msg), "%d ", n);
2840: va_start(ap, fmt);
2841: vsnprintf(msg + b, sizeof(msg) - b, fmt, ap);
1.130 wiz 2842: va_end(ap);
1.158 lukem 2843: cprintf(stdout, "%s\r\n", msg);
1.1 cgd 2844: (void)fflush(stdout);
1.171 christos 2845: if (ftpd_debug)
1.158 lukem 2846: syslog(LOG_DEBUG, "<--- %s", msg);
1.1 cgd 2847: }
2848:
1.8 deraadt 2849: static void
1.118 lukem 2850: logremotehost(struct sockinet *who)
1.1 cgd 2851: {
1.109 lukem 2852:
1.191 christos 2853: #if defined(HAVE_SOCKADDR_SNPRINTF)
2854: char abuf[BUFSIZ];
2855: #endif
2856:
2857: struct sockaddr *sa = (struct sockaddr *)&who->si_su;
2858: if (getnameinfo(sa, who->su_len, remotehost, sizeof(remotehost), NULL,
2859: 0, getnameopts))
1.112 itojun 2860: strlcpy(remotehost, "?", sizeof(remotehost));
1.191 christos 2861: #if defined(HAVE_SOCKADDR_SNPRINTF)
2862: sockaddr_snprintf(abuf, sizeof(abuf), "%a", sa);
2863: snprintf(remoteloghost, sizeof(remoteloghost), "%s(%s)", remotehost,
2864: abuf);
2865: #else
2866: strlcpy(remoteloghost, remotehost, sizeof(remoteloghost));
2867: #endif
2868:
1.181 lukem 2869: #if defined(HAVE_SETPROCTITLE)
1.8 deraadt 2870: snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1.97 itojun 2871: setproctitle("%s", proctitle);
1.181 lukem 2872: #endif /* defined(HAVE_SETPROCTITLE) */
1.8 deraadt 2873: if (logging)
1.79 lukem 2874: syslog(LOG_INFO, "connection from %s to %s",
1.191 christos 2875: remoteloghost, hostname);
1.1 cgd 2876: }
2877:
2878: /*
1.118 lukem 2879: * Record logout in wtmp file and exit with supplied status.
1.158 lukem 2880: * NOTE: because this is called from signal handlers it cannot
2881: * use stdio (or call other functions that use stdio).
1.1 cgd 2882: */
1.8 deraadt 2883: void
1.88 lukem 2884: dologout(int status)
1.1 cgd 2885: {
1.16 mrg 2886: /*
2887: * Prevent reception of SIGURG from resulting in a resumption
2888: * back to the main program loop.
2889: */
2890: transflag = 0;
1.141 christos 2891: logout_utmp();
1.1 cgd 2892: if (logged_in) {
1.36 mycroft 2893: #ifdef KERBEROS
1.4 cgd 2894: if (!notickets && krbtkfile_env)
2895: unlink(krbtkfile_env);
2896: #endif
1.1 cgd 2897: }
2898: /* beware of flushing buffers after a SIGPIPE */
1.154 lukem 2899: if (xferlogfd != -1)
2900: close(xferlogfd);
1.1 cgd 2901: _exit(status);
2902: }
2903:
1.124 lukem 2904: void
2905: abor(void)
1.123 aidan 2906: {
1.124 lukem 2907:
1.158 lukem 2908: if (!transflag)
2909: return;
1.123 aidan 2910: tmpline[0] = '\0';
2911: is_oob = 0;
2912: reply(426, "Transfer aborted. Data connection closed.");
2913: reply(226, "Abort successful");
1.158 lukem 2914: transflag = 0; /* flag that the transfer has aborted */
1.123 aidan 2915: }
2916:
1.124 lukem 2917: void
2918: statxfer(void)
1.123 aidan 2919: {
1.124 lukem 2920:
1.158 lukem 2921: if (!transflag)
2922: return;
1.123 aidan 2923: tmpline[0] = '\0';
2924: is_oob = 0;
2925: if (file_size != (off_t) -1)
2926: reply(213,
2927: "Status: " LLF " of " LLF " byte%s transferred",
2928: (LLT)byte_count, (LLT)file_size,
2929: PLURAL(byte_count));
2930: else
2931: reply(213, "Status: " LLF " byte%s transferred",
2932: (LLT)byte_count, PLURAL(byte_count));
2933: }
2934:
1.158 lukem 2935: /*
2936: * Call when urgflag != 0 to handle Out Of Band commands.
2937: * Returns non zero if the OOB command aborted the transfer
2938: * by setting transflag to 0. (c.f., "ABOR").
2939: */
2940: static int
1.198 ! dholland 2941: handleoobcmd(void)
1.1 cgd 2942: {
2943: char *cp;
1.187 lukem 2944: int ret;
1.1 cgd 2945:
1.158 lukem 2946: if (!urgflag)
2947: return (0);
2948: urgflag = 0;
1.1 cgd 2949: /* only process if transfer occurring */
2950: if (!transflag)
1.158 lukem 2951: return (0);
1.1 cgd 2952: cp = tmpline;
1.194 roy 2953: ret = get_line(cp, sizeof(tmpline)-1, stdin);
1.187 lukem 2954: if (ret == -1) {
1.1 cgd 2955: reply(221, "You could at least say goodbye.");
2956: dologout(0);
1.187 lukem 2957: } else if (ret == -2) {
2958: /* Ignore truncated command */
2959: /* XXX: abort xfer with "500 command too long", & return 1 ? */
2960: return 0;
1.1 cgd 2961: }
1.158 lukem 2962: /*
2963: * Manually parse OOB commands, because we can't
2964: * recursively call the yacc parser...
2965: */
2966: if (strcasecmp(cp, "ABOR\r\n") == 0) {
2967: abor();
2968: } else if (strcasecmp(cp, "STAT\r\n") == 0) {
2969: statxfer();
2970: } else {
2971: /* XXX: error with "500 unknown command" ? */
2972: }
2973: return (transflag == 0);
1.1 cgd 2974: }
2975:
1.84 lukem 2976: static int
1.88 lukem 2977: bind_pasv_addr(void)
1.84 lukem 2978: {
2979: static int passiveport;
2980: int port, len;
2981:
2982: len = pasv_addr.su_len;
2983: if (curclass.portmin == 0 && curclass.portmax == 0) {
2984: pasv_addr.su_port = 0;
1.109 lukem 2985: return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len));
1.84 lukem 2986: }
2987:
2988: if (passiveport == 0) {
2989: srand(getpid());
2990: passiveport = rand() % (curclass.portmax - curclass.portmin)
2991: + curclass.portmin;
2992: }
2993:
2994: port = passiveport;
2995: while (1) {
2996: port++;
2997: if (port > curclass.portmax)
2998: port = curclass.portmin;
2999: else if (port == passiveport) {
3000: errno = EAGAIN;
3001: return (-1);
3002: }
3003: pasv_addr.su_port = htons(port);
1.109 lukem 3004: if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0)
1.84 lukem 3005: break;
3006: if (errno != EADDRINUSE)
3007: return (-1);
3008: }
3009: passiveport = port;
3010: return (0);
3011: }
3012:
1.1 cgd 3013: /*
3014: * Note: a response of 425 is not mentioned as a possible response to
1.8 deraadt 3015: * the PASV command in RFC959. However, it has been blessed as
3016: * a legitimate response by Jon Postel in a telephone conversation
1.1 cgd 3017: * with Rick Adams on 25 Jan 89.
3018: */
1.8 deraadt 3019: void
1.88 lukem 3020: passive(void)
1.1 cgd 3021: {
1.193 lukem 3022: socklen_t len;
3023: int recvbufsize;
1.8 deraadt 3024: char *p, *a;
1.1 cgd 3025:
1.72 itojun 3026: if (pdata >= 0)
3027: close(pdata);
1.1 cgd 3028: pdata = socket(AF_INET, SOCK_STREAM, 0);
1.23 lukem 3029: if (pdata < 0 || !logged_in) {
1.1 cgd 3030: perror_reply(425, "Can't open passive connection");
3031: return;
3032: }
3033: pasv_addr = ctrl_addr;
1.84 lukem 3034:
3035: if (bind_pasv_addr() < 0)
3036: goto pasv_error;
1.67 itojun 3037: len = pasv_addr.su_len;
1.109 lukem 3038: if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
1.1 cgd 3039: goto pasv_error;
1.109 lukem 3040: pasv_addr.su_len = len;
1.168 ginsbach 3041: if (curclass.recvbufsize) {
3042: recvbufsize = curclass.recvbufsize;
3043: if (setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, &recvbufsize,
3044: sizeof(int)) == -1)
3045: syslog(LOG_WARNING, "setsockopt(SO_RCVBUF, %d): %m",
3046: recvbufsize);
3047: }
1.1 cgd 3048: if (listen(pdata, 1) < 0)
3049: goto pasv_error;
1.118 lukem 3050: if (curclass.advertise.su_len != 0)
3051: a = (char *) &curclass.advertise.su_addr;
3052: else
3053: a = (char *) &pasv_addr.su_addr;
1.67 itojun 3054: p = (char *) &pasv_addr.su_port;
1.1 cgd 3055:
3056: #define UC(b) (((int) b) & 0xff)
3057:
3058: reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
3059: UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
3060: return;
3061:
1.89 lukem 3062: pasv_error:
1.67 itojun 3063: (void) close(pdata);
3064: pdata = -1;
3065: perror_reply(425, "Can't open passive connection");
3066: return;
3067: }
3068:
3069: /*
1.106 itojun 3070: * convert protocol identifier to/from AF
3071: */
3072: int
3073: lpsvproto2af(int proto)
3074: {
3075:
3076: switch (proto) {
1.109 lukem 3077: case 4:
3078: return AF_INET;
1.106 itojun 3079: #ifdef INET6
1.109 lukem 3080: case 6:
3081: return AF_INET6;
1.106 itojun 3082: #endif
1.109 lukem 3083: default:
3084: return -1;
1.106 itojun 3085: }
3086: }
3087:
3088: int
3089: af2lpsvproto(int af)
3090: {
3091:
3092: switch (af) {
1.109 lukem 3093: case AF_INET:
3094: return 4;
1.106 itojun 3095: #ifdef INET6
1.109 lukem 3096: case AF_INET6:
3097: return 6;
1.106 itojun 3098: #endif
1.109 lukem 3099: default:
3100: return -1;
1.106 itojun 3101: }
3102: }
3103:
3104: int
3105: epsvproto2af(int proto)
3106: {
3107:
3108: switch (proto) {
1.109 lukem 3109: case 1:
3110: return AF_INET;
1.106 itojun 3111: #ifdef INET6
1.109 lukem 3112: case 2:
3113: return AF_INET6;
1.106 itojun 3114: #endif
1.109 lukem 3115: default:
3116: return -1;
1.106 itojun 3117: }
3118: }
3119:
3120: int
3121: af2epsvproto(int af)
3122: {
3123:
3124: switch (af) {
1.109 lukem 3125: case AF_INET:
3126: return 1;
1.106 itojun 3127: #ifdef INET6
1.109 lukem 3128: case AF_INET6:
3129: return 2;
1.106 itojun 3130: #endif
1.109 lukem 3131: default:
3132: return -1;
1.106 itojun 3133: }
3134: }
3135:
3136: /*
1.67 itojun 3137: * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3138: * 229 Entering Extended Passive Mode (|||port|)
3139: */
3140: void
1.193 lukem 3141: long_passive(const char *cmd, int pf)
1.67 itojun 3142: {
1.176 mrg 3143: socklen_t len;
1.84 lukem 3144: char *p, *a;
1.67 itojun 3145:
3146: if (!logged_in) {
3147: syslog(LOG_NOTICE, "long passive but not logged in");
3148: reply(503, "Login with USER first.");
3149: return;
3150: }
3151:
1.106 itojun 3152: if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
3153: /*
1.111 lukem 3154: * XXX: only EPRT/EPSV ready clients will understand this
1.106 itojun 3155: */
3156: if (strcmp(cmd, "EPSV") != 0)
3157: reply(501, "Network protocol mismatch"); /*XXX*/
3158: else
3159: epsv_protounsupp("Network protocol mismatch");
1.67 itojun 3160:
1.106 itojun 3161: return;
1.67 itojun 3162: }
1.73 lukem 3163:
1.72 itojun 3164: if (pdata >= 0)
3165: close(pdata);
1.67 itojun 3166: pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
3167: if (pdata < 0) {
3168: perror_reply(425, "Can't open passive connection");
3169: return;
3170: }
3171: pasv_addr = ctrl_addr;
1.84 lukem 3172: if (bind_pasv_addr() < 0)
1.67 itojun 3173: goto pasv_error;
3174: len = pasv_addr.su_len;
1.109 lukem 3175: if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
1.67 itojun 3176: goto pasv_error;
1.109 lukem 3177: pasv_addr.su_len = len;
1.67 itojun 3178: if (listen(pdata, 1) < 0)
3179: goto pasv_error;
3180: p = (char *) &pasv_addr.su_port;
3181:
3182: #define UC(b) (((int) b) & 0xff)
3183:
3184: if (strcmp(cmd, "LPSV") == 0) {
1.118 lukem 3185: struct sockinet *advert;
3186:
3187: if (curclass.advertise.su_len != 0)
3188: advert = &curclass.advertise;
3189: else
3190: advert = &pasv_addr;
3191: switch (advert->su_family) {
1.67 itojun 3192: case AF_INET:
1.118 lukem 3193: a = (char *) &advert->su_addr;
1.109 lukem 3194: reply(228,
3195: "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
1.67 itojun 3196: 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3197: 2, UC(p[0]), UC(p[1]));
3198: return;
1.104 christos 3199: #ifdef INET6
1.67 itojun 3200: case AF_INET6:
1.118 lukem 3201: a = (char *) &advert->su_6addr;
1.109 lukem 3202: reply(228,
3203: "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)",
1.67 itojun 3204: 6, 16,
3205: UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3206: UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
3207: UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
3208: UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
3209: 2, UC(p[0]), UC(p[1]));
3210: return;
1.104 christos 3211: #endif
1.67 itojun 3212: }
3213: #undef UC
3214: } else if (strcmp(cmd, "EPSV") == 0) {
3215: switch (pasv_addr.su_family) {
3216: case AF_INET:
1.109 lukem 3217: #ifdef INET6
1.67 itojun 3218: case AF_INET6:
1.109 lukem 3219: #endif
1.67 itojun 3220: reply(229, "Entering Extended Passive Mode (|||%d|)",
1.109 lukem 3221: ntohs(pasv_addr.su_port));
1.67 itojun 3222: return;
3223: }
3224: } else {
3225: /* more proper error code? */
3226: }
3227:
1.89 lukem 3228: pasv_error:
1.1 cgd 3229: (void) close(pdata);
3230: pdata = -1;
3231: perror_reply(425, "Can't open passive connection");
3232: return;
1.106 itojun 3233: }
3234:
3235: int
3236: extended_port(const char *arg)
3237: {
3238: char *tmp = NULL;
3239: char *result[3];
3240: char *p, *q;
3241: char delim;
3242: struct addrinfo hints;
3243: struct addrinfo *res = NULL;
3244: int i;
3245: unsigned long proto;
3246:
1.171 christos 3247: tmp = ftpd_strdup(arg);
1.106 itojun 3248: p = tmp;
3249: delim = p[0];
3250: p++;
3251: memset(result, 0, sizeof(result));
3252: for (i = 0; i < 3; i++) {
3253: q = strchr(p, delim);
3254: if (!q || *q != delim)
3255: goto parsefail;
3256: *q++ = '\0';
3257: result[i] = p;
3258: p = q;
3259: }
3260:
1.111 lukem 3261: /* some more sanity checks */
1.146 itojun 3262: errno = 0;
1.106 itojun 3263: p = NULL;
3264: (void)strtoul(result[2], &p, 10);
1.146 itojun 3265: if (errno || !*result[2] || *p)
1.108 itojun 3266: goto parsefail;
1.146 itojun 3267: errno = 0;
1.106 itojun 3268: p = NULL;
3269: proto = strtoul(result[0], &p, 10);
1.146 itojun 3270: if (errno || !*result[0] || *p)
1.106 itojun 3271: goto protounsupp;
3272:
3273: memset(&hints, 0, sizeof(hints));
3274: hints.ai_family = epsvproto2af((int)proto);
3275: if (hints.ai_family < 0)
3276: goto protounsupp;
3277: hints.ai_socktype = SOCK_STREAM;
3278: hints.ai_flags = AI_NUMERICHOST;
3279: if (getaddrinfo(result[1], result[2], &hints, &res))
3280: goto parsefail;
3281: if (res->ai_next)
3282: goto parsefail;
3283: if (sizeof(data_dest) < res->ai_addrlen)
3284: goto parsefail;
1.118 lukem 3285: memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen);
3286: data_dest.su_len = res->ai_addrlen;
1.113 lukem 3287: #ifdef INET6
1.106 itojun 3288: if (his_addr.su_family == AF_INET6 &&
3289: data_dest.su_family == AF_INET6) {
1.111 lukem 3290: /* XXX: more sanity checks! */
1.109 lukem 3291: data_dest.su_scope_id = his_addr.su_scope_id;
1.106 itojun 3292: }
1.113 lukem 3293: #endif
1.106 itojun 3294:
3295: if (tmp != NULL)
3296: free(tmp);
3297: if (res)
3298: freeaddrinfo(res);
3299: return 0;
3300:
1.109 lukem 3301: parsefail:
1.106 itojun 3302: reply(500, "Invalid argument, rejected.");
3303: usedefault = 1;
1.107 itojun 3304: if (tmp != NULL)
3305: free(tmp);
3306: if (res)
3307: freeaddrinfo(res);
1.106 itojun 3308: return -1;
3309:
1.109 lukem 3310: protounsupp:
1.106 itojun 3311: epsv_protounsupp("Protocol not supported");
3312: usedefault = 1;
1.107 itojun 3313: if (tmp != NULL)
3314: free(tmp);
1.106 itojun 3315: return -1;
3316: }
3317:
3318: /*
3319: * 522 Protocol not supported (proto,...)
3320: * as we assume address family for control and data connections are the same,
3321: * we do not return the list of address families we support - instead, we
3322: * return the address family of the control connection.
3323: */
3324: void
3325: epsv_protounsupp(const char *message)
3326: {
3327: int proto;
3328:
3329: proto = af2epsvproto(ctrl_addr.su_family);
3330: if (proto < 0)
1.111 lukem 3331: reply(501, "%s", message); /* XXX */
1.106 itojun 3332: else
3333: reply(522, "%s, use (%d)", message, proto);
1.1 cgd 3334: }
3335:
3336: /*
3337: * Generate unique name for file with basename "local".
3338: * The file named "local" is already known to exist.
3339: * Generates failure reply on error.
1.29 mrg 3340: *
1.109 lukem 3341: * XXX: this function should under go changes similar to
3342: * the mktemp(3)/mkstemp(3) changes.
1.1 cgd 3343: */
1.8 deraadt 3344: static char *
1.88 lukem 3345: gunique(const char *local)
1.1 cgd 3346: {
1.82 lukem 3347: static char new[MAXPATHLEN];
1.1 cgd 3348: struct stat st;
1.8 deraadt 3349: char *cp;
1.73 lukem 3350: int count;
1.1 cgd 3351:
1.8 deraadt 3352: cp = strrchr(local, '/');
1.1 cgd 3353: if (cp)
3354: *cp = '\0';
3355: if (stat(cp ? local : ".", &st) < 0) {
3356: perror_reply(553, cp ? local : ".");
1.57 lukem 3357: return (NULL);
1.1 cgd 3358: }
3359: if (cp)
3360: *cp = '/';
3361: for (count = 1; count < 100; count++) {
1.73 lukem 3362: (void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count);
1.1 cgd 3363: if (stat(new, &st) < 0)
1.8 deraadt 3364: return (new);
1.1 cgd 3365: }
3366: reply(452, "Unique file name cannot be created.");
1.8 deraadt 3367: return (NULL);
1.1 cgd 3368: }
3369:
3370: /*
3371: * Format and send reply containing system error number.
3372: */
1.8 deraadt 3373: void
1.88 lukem 3374: perror_reply(int code, const char *string)
1.1 cgd 3375: {
1.62 lukem 3376: int save_errno;
1.8 deraadt 3377:
1.62 lukem 3378: save_errno = errno;
1.1 cgd 3379: reply(code, "%s: %s.", string, strerror(errno));
1.62 lukem 3380: errno = save_errno;
1.1 cgd 3381: }
3382:
3383: static char *onefile[] = {
1.193 lukem 3384: NULL,
1.1 cgd 3385: 0
3386: };
3387:
1.8 deraadt 3388: void
1.88 lukem 3389: send_file_list(const char *whichf)
1.1 cgd 3390: {
3391: struct stat st;
1.178 christos 3392: DIR *dirp;
1.1 cgd 3393: struct dirent *dir;
1.178 christos 3394: FILE *volatile dout;
3395: char **volatile dirlist;
3396: char *dirname, *p;
3397: char *notglob;
3398: int volatile simple;
3399: int volatile freeglob;
1.8 deraadt 3400: glob_t gl;
1.62 lukem 3401:
1.178 christos 3402: dirp = NULL;
3403: dout = NULL;
3404: notglob = NULL;
3405: simple = 0;
3406: freeglob = 0;
1.158 lukem 3407: urgflag = 0;
1.1 cgd 3408:
1.62 lukem 3409: p = NULL;
1.8 deraadt 3410: if (strpbrk(whichf, "~{[*?") != NULL) {
1.122 christos 3411: int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
1.1 cgd 3412:
1.8 deraadt 3413: memset(&gl, 0, sizeof(gl));
3414: freeglob = 1;
3415: if (glob(whichf, flags, 0, &gl)) {
1.170 lukem 3416: reply(450, "Not found");
1.158 lukem 3417: goto cleanup_send_file_list;
1.8 deraadt 3418: } else if (gl.gl_pathc == 0) {
1.1 cgd 3419: errno = ENOENT;
1.170 lukem 3420: perror_reply(450, whichf);
1.158 lukem 3421: goto cleanup_send_file_list;
1.1 cgd 3422: }
1.8 deraadt 3423: dirlist = gl.gl_pathv;
1.1 cgd 3424: } else {
1.171 christos 3425: notglob = ftpd_strdup(whichf);
1.132 lukem 3426: onefile[0] = notglob;
1.1 cgd 3427: dirlist = onefile;
3428: simple = 1;
3429: }
1.62 lukem 3430: /* XXX: } for vi sm */
1.1 cgd 3431:
1.20 lukem 3432: while ((dirname = *dirlist++) != NULL) {
1.33 lukem 3433: int trailingslash = 0;
3434:
1.1 cgd 3435: if (stat(dirname, &st) < 0) {
3436: /*
3437: * If user typed "ls -l", etc, and the client
3438: * used NLST, do what the user meant.
3439: */
1.71 lukem 3440: /* XXX: nuke this support? */
1.1 cgd 3441: if (dirname[0] == '-' && *dirlist == NULL &&
3442: transflag == 0) {
1.193 lukem 3443: const char *argv[] = { INTERNAL_LS, "", NULL };
1.71 lukem 3444:
3445: argv[1] = dirname;
3446: retrieve(argv, dirname);
1.158 lukem 3447: goto cleanup_send_file_list;
1.1 cgd 3448: }
1.170 lukem 3449: perror_reply(450, whichf);
1.95 lukem 3450: goto cleanup_send_file_list;
1.1 cgd 3451: }
3452:
1.8 deraadt 3453: if (S_ISREG(st.st_mode)) {
1.118 lukem 3454: /*
3455: * XXXRFC:
3456: * should we follow RFC959 and not work
3457: * for non directories?
3458: */
1.1 cgd 3459: if (dout == NULL) {
3460: dout = dataconn("file list", (off_t)-1, "w");
3461: if (dout == NULL)
1.158 lukem 3462: goto cleanup_send_file_list;
3463: transflag = 1;
1.1 cgd 3464: }
1.118 lukem 3465: cprintf(dout, "%s%s\n", dirname,
1.62 lukem 3466: type == TYPE_A ? "\r" : "");
1.1 cgd 3467: continue;
1.8 deraadt 3468: } else if (!S_ISDIR(st.st_mode))
1.1 cgd 3469: continue;
3470:
1.33 lukem 3471: if (dirname[strlen(dirname) - 1] == '/')
3472: trailingslash++;
3473:
1.1 cgd 3474: if ((dirp = opendir(dirname)) == NULL)
3475: continue;
3476:
3477: while ((dir = readdir(dirp)) != NULL) {
1.82 lukem 3478: char nbuf[MAXPATHLEN];
1.1 cgd 3479:
1.158 lukem 3480: if (urgflag && handleoobcmd())
3481: goto cleanup_send_file_list;
3482:
1.93 lukem 3483: if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
1.1 cgd 3484: continue;
3485:
1.33 lukem 3486: (void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
3487: trailingslash ? "" : "/", dir->d_name);
1.1 cgd 3488:
3489: /*
1.30 lukem 3490: * We have to do a stat to ensure it's
1.1 cgd 3491: * not a directory or special file.
3492: */
1.118 lukem 3493: /*
3494: * XXXRFC:
3495: * should we follow RFC959 and filter out
3496: * non files ? lukem - NO!, or not until
3497: * our ftp client uses MLS{T,D} for completion.
3498: */
1.1 cgd 3499: if (simple || (stat(nbuf, &st) == 0 &&
1.8 deraadt 3500: S_ISREG(st.st_mode))) {
1.1 cgd 3501: if (dout == NULL) {
3502: dout = dataconn("file list", (off_t)-1,
3503: "w");
3504: if (dout == NULL)
1.158 lukem 3505: goto cleanup_send_file_list;
3506: transflag = 1;
1.1 cgd 3507: }
1.62 lukem 3508: p = nbuf;
1.1 cgd 3509: if (nbuf[0] == '.' && nbuf[1] == '/')
1.62 lukem 3510: p = &nbuf[2];
1.118 lukem 3511: cprintf(dout, "%s%s\n", p,
1.62 lukem 3512: type == TYPE_A ? "\r" : "");
1.1 cgd 3513: }
3514: }
3515: (void) closedir(dirp);
3516: }
3517:
3518: if (dout == NULL)
1.170 lukem 3519: reply(450, "No files found.");
1.1 cgd 3520: else if (ferror(dout) != 0)
1.170 lukem 3521: perror_reply(451, "Data connection");
1.1 cgd 3522: else
3523: reply(226, "Transfer complete.");
3524:
1.95 lukem 3525: cleanup_send_file_list:
1.158 lukem 3526: closedataconn(dout);
1.1 cgd 3527: transflag = 0;
1.158 lukem 3528: urgflag = 0;
1.62 lukem 3529: total_xfers++;
3530: total_xfers_out++;
1.132 lukem 3531: if (notglob)
3532: free(notglob);
1.62 lukem 3533: if (freeglob)
1.8 deraadt 3534: globfree(&gl);
1.34 lukem 3535: }
3536:
3537: char *
1.88 lukem 3538: conffilename(const char *s)
1.34 lukem 3539: {
1.82 lukem 3540: static char filename[MAXPATHLEN];
1.34 lukem 3541:
1.73 lukem 3542: if (*s == '/')
3543: strlcpy(filename, s, sizeof(filename));
3544: else
3545: (void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
3546: return (filename);
1.62 lukem 3547: }
3548:
3549: /*
1.118 lukem 3550: * logxfer --
3551: * if logging > 1, then based on the arguments, syslog a message:
1.62 lukem 3552: * if bytes != -1 "<command> <file1> = <bytes> bytes"
3553: * else if file2 != NULL "<command> <file1> <file2>"
3554: * else "<command> <file1>"
3555: * if elapsed != NULL, append "in xxx.yyy seconds"
3556: * if error != NULL, append ": " + error
1.118 lukem 3557: *
1.147 lukem 3558: * if doxferlog != 0, bytes != -1, and command is "get", "put",
1.154 lukem 3559: * or "append", syslog and/or write a wu-ftpd style xferlog entry
1.62 lukem 3560: */
3561: void
1.118 lukem 3562: logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
1.139 enami 3563: const struct timeval *elapsed, const char *error)
1.62 lukem 3564: {
1.169 lukem 3565: char buf[MAXPATHLEN * 2 + 100];
3566: char realfile1[MAXPATHLEN], realfile2[MAXPATHLEN];
1.118 lukem 3567: const char *r1, *r2;
3568: char direction;
3569: size_t len;
3570: time_t now;
1.62 lukem 3571:
1.118 lukem 3572: if (logging <=1 && !doxferlog)
1.62 lukem 3573: return;
3574:
1.118 lukem 3575: r1 = r2 = NULL;
1.169 lukem 3576: if ((r1 = realpath(file1, realfile1)) == NULL)
1.118 lukem 3577: r1 = file1;
3578: if (file2 != NULL)
1.169 lukem 3579: if ((r2 = realpath(file2, realfile2)) == NULL)
1.118 lukem 3580: r2 = file2;
1.62 lukem 3581:
1.118 lukem 3582: /*
3583: * syslog command
3584: */
3585: if (logging > 1) {
3586: len = snprintf(buf, sizeof(buf), "%s %s", command, r1);
3587: if (bytes != (off_t)-1)
3588: len += snprintf(buf + len, sizeof(buf) - len,
3589: " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes));
3590: else if (r2 != NULL)
3591: len += snprintf(buf + len, sizeof(buf) - len,
3592: " %s", r2);
3593: if (elapsed != NULL)
3594: len += snprintf(buf + len, sizeof(buf) - len,
1.192 lukem 3595: " in " LLF ".%.03ld seconds",
3596: (LLT)elapsed->tv_sec,
1.190 christos 3597: (long)(elapsed->tv_usec / 1000));
1.118 lukem 3598: if (error != NULL)
3599: len += snprintf(buf + len, sizeof(buf) - len,
3600: ": %s", error);
3601: syslog(LOG_INFO, "%s", buf);
1.62 lukem 3602: }
3603:
1.118 lukem 3604: /*
3605: * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3606: */
1.138 lukem 3607: if (!doxferlog || bytes == -1)
1.118 lukem 3608: return;
3609:
3610: if (strcmp(command, "get") == 0)
3611: direction = 'o';
3612: else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0)
3613: direction = 'i';
3614: else
3615: return;
1.62 lukem 3616:
1.118 lukem 3617: time(&now);
1.154 lukem 3618: len = snprintf(buf, sizeof(buf),
1.192 lukem 3619: "%.24s " LLF " %s " LLF " %s %c %s %c %c %s FTP 0 * %c\n",
1.118 lukem 3620:
3621: /*
1.154 lukem 3622: * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
1.118 lukem 3623: * the full date. This may be problematic for accurate log parsing,
3624: * given that syslog messages don't contain the full date.
3625: */
3626: ctime(&now),
1.192 lukem 3627: (LLT)
1.190 christos 3628: (elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0)),
1.118 lukem 3629: remotehost,
1.138 lukem 3630: (LLT) bytes,
1.118 lukem 3631: r1,
3632: type == TYPE_A ? 'a' : 'b',
3633: "_", /* XXX: take conversions into account? */
3634: direction,
3635:
3636: curclass.type == CLASS_GUEST ? 'a' :
3637: curclass.type == CLASS_CHROOT ? 'g' :
3638: curclass.type == CLASS_REAL ? 'r' : '?',
3639:
3640: curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
3641: error != NULL ? 'i' : 'c'
3642: );
1.154 lukem 3643:
3644: if ((doxferlog & 2) && xferlogfd != -1)
3645: write(xferlogfd, buf, len);
3646: if ((doxferlog & 1)) {
3647: buf[len-1] = '\n'; /* strip \n from syslog message */
3648: syslog(LOG_INFO, "xferlog: %s", buf);
3649: }
1.139 enami 3650: }
3651:
3652: /*
3653: * Log the resource usage.
3654: *
3655: * XXX: more resource usage to logging?
3656: */
3657: void
3658: logrusage(const struct rusage *rusage_before,
3659: const struct rusage *rusage_after)
3660: {
3661: struct timeval usrtime, systime;
3662:
3663: if (logging <= 1)
3664: return;
3665:
3666: timersub(&rusage_after->ru_utime, &rusage_before->ru_utime, &usrtime);
3667: timersub(&rusage_after->ru_stime, &rusage_before->ru_stime, &systime);
1.192 lukem 3668: syslog(LOG_INFO, LLF ".%.03ldu " LLF ".%.03lds %ld+%ldio %ldpf+%ldw",
3669: (LLT)usrtime.tv_sec, (long)(usrtime.tv_usec / 1000),
3670: (LLT)systime.tv_sec, (long)(systime.tv_usec / 1000),
1.139 enami 3671: rusage_after->ru_inblock - rusage_before->ru_inblock,
3672: rusage_after->ru_oublock - rusage_before->ru_oublock,
3673: rusage_after->ru_majflt - rusage_before->ru_majflt,
3674: rusage_after->ru_nswap - rusage_before->ru_nswap);
1.115 lukem 3675: }
3676:
3677: /*
1.116 lukem 3678: * Determine if `password' is valid for user given in `pw'.
3679: * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
1.115 lukem 3680: */
3681: int
1.132 lukem 3682: checkpassword(const struct passwd *pwent, const char *password)
1.115 lukem 3683: {
1.189 lukem 3684: const char *orig;
3685: char *new;
1.161 ginsbach 3686: time_t change, expire, now;
1.116 lukem 3687:
1.161 ginsbach 3688: change = expire = 0;
1.132 lukem 3689: if (pwent == NULL)
1.116 lukem 3690: return 1;
3691:
1.161 ginsbach 3692: time(&now);
1.132 lukem 3693: orig = pwent->pw_passwd; /* save existing password */
3694: expire = pwent->pw_expire;
1.189 lukem 3695: change = pwent->pw_change;
3696: if (change == _PASSWORD_CHGNOW)
3697: change = now;
1.116 lukem 3698:
3699: if (orig[0] == '\0') /* don't allow empty passwords */
3700: return 1;
3701:
3702: new = crypt(password, orig); /* encrypt given password */
3703: if (strcmp(new, orig) != 0) /* compare */
3704: return 1;
3705:
1.162 ginsbach 3706: if ((expire && now >= expire) || (change && now >= change))
1.116 lukem 3707: return 2; /* check if expired */
1.115 lukem 3708:
1.116 lukem 3709: return 0; /* OK! */
1.62 lukem 3710: }
3711:
3712: char *
1.171 christos 3713: ftpd_strdup(const char *s)
1.62 lukem 3714: {
3715: char *new = strdup(s);
3716:
3717: if (new == NULL)
3718: fatal("Local resource failure: malloc");
3719: /* NOTREACHED */
3720: return (new);
1.95 lukem 3721: }
3722:
3723: /*
3724: * As per fprintf(), but increment total_bytes and total_bytes_out,
3725: * by the appropriate amount.
3726: */
3727: void
3728: cprintf(FILE *fd, const char *fmt, ...)
3729: {
3730: off_t b;
3731: va_list ap;
3732:
3733: va_start(ap, fmt);
3734: b = vfprintf(fd, fmt, ap);
1.130 wiz 3735: va_end(ap);
1.95 lukem 3736: total_bytes += b;
3737: total_bytes_out += b;
1.1 cgd 3738: }
1.164 christos 3739:
3740: #ifdef USE_PAM
3741: /*
3742: * the following code is stolen from imap-uw PAM authentication module and
3743: * login.c
3744: */
1.182 lukem 3745: typedef struct {
1.183 lukem 3746: const char *uname; /* user name */
3747: int triedonce; /* if non-zero, tried before */
1.182 lukem 3748: } ftpd_cred_t;
1.164 christos 3749:
3750: static int
3751: auth_conv(int num_msg, const struct pam_message **msg,
3752: struct pam_response **resp, void *appdata)
3753: {
1.187 lukem 3754: int i, ret;
1.183 lukem 3755: size_t n;
1.182 lukem 3756: ftpd_cred_t *cred = (ftpd_cred_t *) appdata;
1.164 christos 3757: struct pam_response *myreply;
1.183 lukem 3758: char pbuf[FTP_BUFLEN];
1.164 christos 3759:
1.183 lukem 3760: if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
3761: return (PAM_CONV_ERR);
1.164 christos 3762: myreply = calloc(num_msg, sizeof *myreply);
3763: if (myreply == NULL)
3764: return PAM_BUF_ERR;
3765:
3766: for (i = 0; i < num_msg; i++) {
1.183 lukem 3767: myreply[i].resp_retcode = 0;
3768: myreply[i].resp = NULL;
1.164 christos 3769: switch (msg[i]->msg_style) {
1.183 lukem 3770: case PAM_PROMPT_ECHO_ON: /* user */
3771: myreply[i].resp = ftpd_strdup(cred->uname);
1.164 christos 3772: /* PAM frees resp. */
3773: break;
1.183 lukem 3774: case PAM_PROMPT_ECHO_OFF: /* authtok (password) */
3775: /*
3776: * Only send a single 331 reply and
3777: * then expect a PASS.
3778: */
3779: if (cred->triedonce) {
3780: syslog(LOG_ERR,
3781: "auth_conv: already performed PAM_PROMPT_ECHO_OFF");
3782: goto fail;
3783: }
3784: cred->triedonce++;
3785: if (msg[i]->msg[0] == '\0') {
3786: (void)strlcpy(pbuf, "password", sizeof(pbuf));
3787: } else {
1.185 lukem 3788: /* Uncapitalize msg */
1.183 lukem 3789: (void)strlcpy(pbuf, msg[i]->msg, sizeof(pbuf));
3790: if (isupper((unsigned char)pbuf[0]))
3791: pbuf[0] = tolower(
3792: (unsigned char)pbuf[0]);
1.185 lukem 3793: /* Remove trailing ':' and whitespace */
3794: n = strlen(pbuf);
3795: while (n-- > 0) {
3796: if (isspace((unsigned char)pbuf[n]) ||
3797: pbuf[n] == ':')
3798: pbuf[n] = '\0';
3799: else
3800: break;
3801: }
1.183 lukem 3802: }
3803: /* Send reply, wait for a response. */
3804: reply(331, "User %s accepted, provide %s.",
3805: cred->uname, pbuf);
3806: (void) alarm(curclass.timeout);
1.194 roy 3807: ret = get_line(pbuf, sizeof(pbuf)-1, stdin);
1.187 lukem 3808: (void) alarm(0);
3809: if (ret == -1) {
1.183 lukem 3810: reply(221, "You could at least say goodbye.");
3811: dologout(0);
1.187 lukem 3812: } else if (ret == -2) {
3813: /* XXX: should we do this reply(-530, ..) ? */
3814: reply(-530, "Command too long.");
3815: goto fail;
1.183 lukem 3816: }
3817: /* Ensure it is PASS */
3818: if (strncasecmp(pbuf, "PASS ", 5) != 0) {
1.187 lukem 3819: syslog(LOG_ERR,
3820: "auth_conv: unexpected reply '%.4s'", pbuf);
3821: /* XXX: should we do this reply(-530, ..) ? */
3822: reply(-530, "Unexpected reply '%.4s'.", pbuf);
3823: goto fail;
1.183 lukem 3824: }
3825: /* Strip CRLF from "PASS" reply */
3826: n = strlen(pbuf);
3827: while (--n >= 5 &&
3828: (pbuf[n] == '\r' || pbuf[n] == '\n'))
3829: pbuf[n] = '\0';
3830: /* Copy password into reply */
3831: myreply[i].resp = ftpd_strdup(pbuf+5);
3832: /* PAM frees resp. */
1.164 christos 3833: break;
3834: case PAM_TEXT_INFO:
3835: case PAM_ERROR_MSG:
3836: break;
3837: default: /* unknown message style */
1.183 lukem 3838: goto fail;
1.164 christos 3839: }
3840: }
3841:
3842: *resp = myreply;
3843: return PAM_SUCCESS;
1.183 lukem 3844:
3845: fail:
3846: free(myreply);
3847: *resp = NULL;
3848: return PAM_CONV_ERR;
1.164 christos 3849: }
3850:
3851: /*
3852: * Attempt to authenticate the user using PAM. Returns 0 if the user is
3853: * authenticated, or 1 if not authenticated. If some sort of PAM system
3854: * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3855: * function returns -1. This can be used as an indication that we should
3856: * fall back to a different authentication mechanism.
1.183 lukem 3857: * pw maybe be updated to a new user if PAM_USER changes from curname.
1.164 christos 3858: */
3859: static int
1.183 lukem 3860: auth_pam(void)
1.164 christos 3861: {
3862: const char *tmpl_user;
3863: const void *item;
3864: int rval;
3865: int e;
1.183 lukem 3866: ftpd_cred_t auth_cred = { curname, 0 };
1.164 christos 3867: struct pam_conv conv = { &auth_conv, &auth_cred };
3868:
1.183 lukem 3869: e = pam_start("ftpd", curname, &conv, &pamh);
1.164 christos 3870: if (e != PAM_SUCCESS) {
3871: /*
3872: * In OpenPAM, it's OK to pass NULL to pam_strerror()
3873: * if context creation has failed in the first place.
3874: */
3875: syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e));
3876: return -1;
3877: }
3878:
3879: e = pam_set_item(pamh, PAM_RHOST, remotehost);
3880: if (e != PAM_SUCCESS) {
3881: syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
3882: pam_strerror(pamh, e));
3883: if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3884: syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3885: }
3886: pamh = NULL;
3887: return -1;
3888: }
3889:
1.179 christos 3890: e = pam_set_item(pamh, PAM_SOCKADDR, &his_addr);
3891: if (e != PAM_SUCCESS) {
3892: syslog(LOG_ERR, "pam_set_item(PAM_SOCKADDR): %s",
3893: pam_strerror(pamh, e));
3894: if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3895: syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3896: }
3897: pamh = NULL;
3898: return -1;
3899: }
3900:
1.164 christos 3901: e = pam_authenticate(pamh, 0);
1.185 lukem 3902: if (ftpd_debug)
3903: syslog(LOG_DEBUG, "pam_authenticate: user '%s' returned %d",
3904: curname, e);
1.164 christos 3905: switch (e) {
3906: case PAM_SUCCESS:
3907: /*
3908: * With PAM we support the concept of a "template"
3909: * user. The user enters a login name which is
3910: * authenticated by PAM, usually via a remote service
3911: * such as RADIUS or TACACS+. If authentication
3912: * succeeds, a different but related "template" name
3913: * is used for setting the credentials, shell, and
3914: * home directory. The name the user enters need only
3915: * exist on the remote authentication server, but the
3916: * template name must be present in the local password
3917: * database.
3918: *
3919: * This is supported by two various mechanisms in the
3920: * individual modules. However, from the application's
3921: * point of view, the template user is always passed
3922: * back as a changed value of the PAM_USER item.
3923: */
3924: if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
3925: PAM_SUCCESS) {
3926: tmpl_user = (const char *) item;
1.183 lukem 3927: if (pw == NULL
3928: || strcmp(pw->pw_name, tmpl_user) != 0) {
3929: pw = sgetpwnam(tmpl_user);
3930: if (ftpd_debug)
3931: syslog(LOG_DEBUG,
1.185 lukem 3932: "auth_pam: PAM changed "
3933: "user from '%s' to '%s'",
1.183 lukem 3934: curname, pw->pw_name);
3935: (void)strlcpy(curname, pw->pw_name,
3936: curname_len);
3937: }
1.164 christos 3938: } else
3939: syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
3940: pam_strerror(pamh, e));
3941: rval = 0;
3942: break;
3943:
3944: case PAM_AUTH_ERR:
3945: case PAM_USER_UNKNOWN:
3946: case PAM_MAXTRIES:
3947: rval = 1;
3948: break;
3949:
3950: default:
3951: syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
3952: rval = -1;
3953: break;
3954: }
3955:
3956: if (rval == 0) {
3957: e = pam_acct_mgmt(pamh, 0);
3958: if (e != PAM_SUCCESS) {
3959: syslog(LOG_ERR, "pam_acct_mgmt: %s",
3960: pam_strerror(pamh, e));
3961: rval = 1;
3962: }
3963: }
3964:
3965: if (rval != 0) {
3966: if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3967: syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3968: }
3969: pamh = NULL;
3970: }
3971: return rval;
3972: }
3973:
3974: #endif /* USE_PAM */
CVSweb <webmaster@jp.NetBSD.org>