📄 ftpd.c
字号:
/* * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char copyright[] ="@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n";#endif /* not lint */#ifndef lintstatic char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";#endif /* not lint *//* * FTP server. */#include <sys/param.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#define FTP_NAMES#include <arpa/ftp.h>#include <arpa/inet.h>#include <arpa/telnet.h>#include <ctype.h>#include <dirent.h>#include <err.h>#include <errno.h>#include <fcntl.h>#include <glob.h>#include <limits.h>#include <netdb.h>#include <pwd.h>#include <setjmp.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#include <time.h>#include <unistd.h>#include "pathnames.h"#include "extern.h"#if __STDC__#include <stdarg.h>#else#include <varargs.h>#endifstatic char version[] = "Version 6.00";extern off_t restart_point;extern char cbuf[];struct sockaddr_in ctrl_addr;struct sockaddr_in data_source;struct sockaddr_in data_dest;struct sockaddr_in his_addr;struct sockaddr_in pasv_addr;int data;jmp_buf errcatch, urgcatch;int logged_in;struct passwd *pw;int debug;int timeout = 900; /* timeout after 15 minutes of inactivity */int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */int logging;int guest;int type;int form;int stru; /* avoid C keyword */int mode;int usedefault = 1; /* for data transfers */int pdata = -1; /* for passive mode */sig_atomic_t transflag;off_t file_size;off_t byte_count;#if !defined(CMASK) || CMASK == 0#undef CMASK#define CMASK 027#endifint defumask = CMASK; /* default umask value */char tmpline[7];char hostname[MAXHOSTNAMELEN];char remotehost[MAXHOSTNAMELEN];/* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */#define SWAITMAX 90 /* wait at most 90 seconds */#define SWAITINT 5 /* interval between retries */int swaitmax = SWAITMAX;int swaitint = SWAITINT;#ifdef SETPROCTITLEchar **Argv = NULL; /* pointer to argument vector */char *LastArgv = NULL; /* end of argv */char proctitle[LINE_MAX]; /* initial part of title */#endif /* SETPROCTITLE */#define LOGCMD(cmd, file) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file);#define LOGCMD2(cmd, file1, file2) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ *(file1) == '/' ? "" : curdir(), file1, \ *(file2) == '/' ? "" : curdir(), file2);#define LOGBYTES(cmd, file, cnt) \ if (logging > 1) { \ if (cnt == (off_t)-1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); \ else \ syslog(LOG_INFO, "%s %s%s = %qd bytes", \ cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ }static void ack __P((char *));static void myoob __P((int));static int checkuser __P((char *));static FILE *dataconn __P((char *, off_t, char *));static void dolog __P((struct sockaddr_in *));static char *curdir __P((void));static void end_login __P((void));static FILE *getdatasock __P((char *));static char *gunique __P((char *));static void lostconn __P((int));static int receive_data __P((FILE *, FILE *));static void send_data __P((FILE *, FILE *, off_t));static struct passwd * sgetpwnam __P((char *));static char *sgetsave __P((char *));static char *curdir(){ static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ if (getcwd(path, sizeof(path)-2) == NULL) return (""); if (path[1] != '\0') /* special case for root dir. */ strcat(path, "/"); /* For guest account, skip / since it's chrooted */ return (guest ? path+1 : path);}intmain(argc, argv, envp) int argc; char *argv[]; char **envp;{ int addrlen, ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; /* * LOG_NDELAY sets up the logging connection immediately, * necessary for anonymous ftp's that chroot and can't do it later. */ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); }#ifdef IP_TOS tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");#endif data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); debug = 0;#ifdef SETPROCTITLE /* * Save start and extent of argv for setproctitle. */ Argv = argv; while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]);#endif /* SETPROCTITLE */ while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { switch (ch) { case 'd': debug = 1; break; case 'l': logging++; /* > 1 == extra logging */ break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': { long val = 0; val = strtol(optarg, &optarg, 8); if (*optarg != '\0' || val < 0) warnx("bad value for -u"); else defumask = val; break; } case 'v': debug = 1; break; default: warnx("unknown flag -%c ignored", optopt); break; } } (void) freopen(_PATH_DEVNULL, "w", stderr); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if ((int)signal(SIGURG, myoob) < 0) syslog(LOG_ERR, "signal: %m"); /* Try to handle urgent data inline */#ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt: %m");#endif#ifdef F_SETOWN if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m");#endif dolog(&his_addr); /* * Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; /* If logins are disabled, print out the message. */ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(530, "%s", line); } (void) fflush(stdout); (void) fclose(fd); reply(530, "System not available."); exit(0); } if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(220, "%s", line); } (void) fflush(stdout); (void) fclose(fd); /* reply(220,) must follow */ } (void) gethostname(hostname, sizeof(hostname)); reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */}static voidlostconn(signo) int signo;{ if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(-1);}static char ttyline[20];/* * Helper function for sgetpwnam(). */static char *sgetsave(s) char *s;{ char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new);}/* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */static struct passwd *sgetpwnam(name) char *name;{ static struct passwd save; struct passwd *p; if ((p = getpwnam(name)) == NULL) return (p); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save);}static int login_attempts; /* number of failed login attempts */static int askpasswd; /* had user command, ask for passwd */static char curname[10]; /* current USER name *//* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */voiduser(name) char *name;{ char *cp, *shell; if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } end_login(); } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if (checkuser("ftp") || checkuser("anonymous")) reply(530, "User %s access denied.", name); else if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; askpasswd = 1; reply(331, "Guest login ok, type your name as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) syslog(LOG_NOTICE, "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } if (pw = sgetpwnam(name)) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(name)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = (struct passwd *) NULL; return; } } if (logging) strncpy(curname, name, sizeof(curname)-1); reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts);}/* * Check if a user is in the file _PATH_FTPUSERS */static intcheckuser(name) char *name;{ FILE *fd; int found = 0; char *p, line[BUFSIZ]; if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; if (strcmp(p, name) == 0) { found = 1; break; } } (void) fclose(fd); } return (found);}/* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */static voidend_login(){ (void) seteuid((uid_t)0); if (logged_in) logwtmp(ttyline, "", ""); pw = NULL; logged_in = 0; guest = 0;}voidpass(passwd) char *passwd;{ char *salt, *xpasswd; FILE *fd; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) salt = "xx"; else salt = pw->pw_passwd; xpasswd = crypt(passwd, salt); /* The strcmp does not catch null passwords! */ if (pw == NULL || *pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { reply(530, "Login incorrect."); if (logging) syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", remotehost, curname); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); exit(0); } return; } } login_attempts = 0; /* this time successful */ if (setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -