⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpd_c.txt

📁 基于UNIX的FTP源代码
💻 TXT
📖 第 1 页 / 共 5 页
字号:
		union sockunion tmp_addr;
		const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);

		tmp_addr = his_addr;
		memset(&his_addr, 0, sizeof(his_addr));
		his_addr.su_sin.sin_family = AF_INET;
		his_addr.su_sin.sin_len = sizeof(his_addr.su_sin);
		memcpy(&his_addr.su_sin.sin_addr,
		    &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
		    sizeof(his_addr.su_sin.sin_addr));
		his_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;

		tmp_addr = ctrl_addr;
		memset(&ctrl_addr, 0, sizeof(ctrl_addr));
		ctrl_addr.su_sin.sin_family = AF_INET;
		ctrl_addr.su_sin.sin_len = sizeof(ctrl_addr.su_sin);
		memcpy(&ctrl_addr.su_sin.sin_addr,
		    &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
		    sizeof(ctrl_addr.su_sin.sin_addr));
		ctrl_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;
#else
		while (fgets(line, sizeof(line), fd) != NULL) {
			line[strcspn(line, "\n")] = '\0';
			lreply(530, "%s", line);
		}
		(void) fflush(stdout);
		(void) close(fd);
		reply(530,
			"Connection from IPv4 mapped address is not supported.");
		exit(0);
#endif
	}
#ifdef IP_TOS
	if (his_addr.su_family == AF_INET) {
		tos = IPTOS_LOWDELAY;
		if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos,
		    sizeof(int)) < 0)
			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
	}
#endif
	data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);

	/* Try to handle urgent data inline */
#ifdef SO_OOBINLINE
	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
		syslog(LOG_ERR, "setsockopt: %m");
#endif

	dolog((struct sockaddr *)&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 ((fp = fopen(_PATH_NOLOGIN, "r")) != NULL) {
		while (fgets(line, sizeof(line), fp) != NULL) {
			line[strcspn(line, "\n")] = '\0';
			lreply(530, "%s", line);
		}
		(void) fflush(stdout);
		(void) fclose(fp);
		reply(530, "System not available.");
		exit(0);
	}
	if ((fp = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
		while (fgets(line, sizeof(line), fp) != NULL) {
			line[strcspn(line, "\n")] = '\0';
			lreply(220, "%s", line);
		}
		(void) fflush(stdout);
		(void) fclose(fp);
		/* reply(220,) must follow */
	}
	(void) gethostname(hostname, sizeof(hostname));

	/* Make sure hostname is fully qualified. */
	hp = gethostbyname(hostname);
	if (hp != NULL)
		strlcpy(hostname, hp->h_name, sizeof(hostname));

	if (multihome) {
		error = getnameinfo((struct sockaddr *)&ctrl_addr,
		    ctrl_addr.su_len, dhostname, sizeof(dhostname), NULL, 0, 0);
	}

	if (error != 0)
		reply(220, "FTP server ready.");
	else
		reply(220, "%s FTP server ready.",
		    (multihome ? dhostname : hostname));

	monitor_init();

	for (;;)
		(void) yyparse();
	/* NOTREACHED */
}

/*
 * Signal handlers.
 */
/*ARGSUSED*/
static void
lostconn(int signo)
{
	struct syslog_data sdata = SYSLOG_DATA_INIT;

	sdata.log_fac = LOG_FTP;
	if (debug)
		syslog_r(LOG_DEBUG, &sdata, "lost connection");
	dologout(1);
}

static void
sigquit(int signo)
{
	struct syslog_data sdata = SYSLOG_DATA_INIT;

	sdata.log_fac = LOG_FTP;
	syslog_r(LOG_DEBUG, &sdata, "got signal %s", sys_signame[signo]);
	dologout(1);
}

/*
 * 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(char *name, struct passwd *pw)
{
	static struct passwd *save;
	struct passwd *old;

	if (pw == NULL && (pw = getpwnam(name)) == NULL)
		return (NULL);
	old = save;
	save = pw_dup(pw);
	if (save == NULL) {
		perror_reply(421, "Local resource failure: malloc");
		dologout(1);
		/* NOTREACHED */
	}
	if (old) {
		memset(old->pw_passwd, 0, strlen(old->pw_passwd));
		free(old);
	}
	return (save);
}

static int login_attempts;	/* number of failed login attempts */
static int askpasswd;		/* had user command, ask for passwd */
static char curname[MAXLOGNAME];	/* 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.
 */
void
user(char *name)
{
	char *cp, *shell, *style, *host;
	char *class = NULL;

	if (logged_in) {
		kill_slave("user already logged in");
		end_login();
	}

	/* Close session from previous user if there was one. */
	if (as) {
		auth_close(as);
		as = NULL;
	}
	if (lc) {
		login_close(lc);
		lc = NULL;
	}

	if ((style = strchr(name, ':')) != NULL)
		*style++ = 0;

	guest = 0;
	host = multihome ? dhostname : hostname;
	if (anon_ok &&
	    (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0)) {
		if (checkuser(_PATH_FTPUSERS, "ftp") ||
		    checkuser(_PATH_FTPUSERS, "anonymous"))
			reply(530, "User %s access denied.", name);
		else if ((pw = sgetpwnam("ftp", NULL)) != NULL) {
			guest = 1;
			askpasswd = 1;
			lc = login_getclass(pw->pw_class);
			if ((as = auth_open()) == NULL ||
			    auth_setpwd(as, pw) != 0 ||
			    auth_setoption(as, "FTPD_HOST", host) < 0) {
				if (as) {
					auth_close(as);
					as = NULL;
				}
				login_close(lc);
				lc = NULL;
				reply(421, "Local resource failure");
				return;
			}
			reply(331,
			"Guest login ok, send your email address as password.");
		} else
			reply(530, "User %s unknown.", name);
		if (!askpasswd && logging)
			syslog(LOG_NOTICE,
			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
		return;
	}

	shell = _PATH_BSHELL;
	if ((pw = sgetpwnam(name, NULL))) {
		class = pw->pw_class;
		if (pw->pw_shell != NULL && *pw->pw_shell != '\0')
			shell = pw->pw_shell;
		while ((cp = getusershell()) != NULL)
			if (strcmp(cp, shell) == 0)
				break;
		shell = cp;
		endusershell();
	}

	/* Get login class; if invalid style treat like unknown user. */
	lc = login_getclass(class);
	if (lc && (style = login_getstyle(lc, style, "auth-ftp")) == NULL) {
		login_close(lc);
		lc = NULL;
		pw = NULL;
	}

	/* Do pre-authentication setup. */
	if (lc && ((as = auth_open()) == NULL ||
	    (pw != NULL && auth_setpwd(as, pw) != 0) ||
	    auth_setitem(as, AUTHV_STYLE, style) < 0 ||
	    auth_setitem(as, AUTHV_NAME, name) < 0 ||
	    auth_setitem(as, AUTHV_CLASS, class) < 0 ||
	    auth_setoption(as, "login", "yes") < 0 ||
	    auth_setoption(as, "notickets", "yes") < 0 ||
	    auth_setoption(as, "FTPD_HOST", host) < 0)) {
		if (as) {
			auth_close(as);
			as = NULL;
		}
		login_close(lc);
		lc = NULL;
		reply(421, "Local resource failure");
		return;
	}
	if (logging)
		strlcpy(curname, name, sizeof(curname));

	dochroot = (lc && login_getcapbool(lc, "ftp-chroot", 0)) ||
	    checkuser(_PATH_FTPCHROOT, name);
	if (anon_only && !dochroot) {
		if (anon_ok)
			reply(530, "Sorry, only anonymous ftp allowed.");
		else
			reply(530, "User %s access denied.", name);
		return;
	}
	if (pw) {
		if ((!shell && !dochroot) || checkuser(_PATH_FTPUSERS, name)) {
			reply(530, "User %s access denied.", name);
			if (logging)
				syslog(LOG_NOTICE,
				    "FTP LOGIN REFUSED FROM %s, %s",
				    remotehost, name);
			pw = NULL;
			return;
		}
	}

	if (as != NULL && (cp = auth_challenge(as)) != NULL)
		reply(331, "%s", cp);
	else
		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 "fname"
 */
static int
checkuser(char *fname, char *name)
{
	FILE *fp;
	int found = 0;
	char *p, line[BUFSIZ];

	if ((fp = fopen(fname, "r")) != NULL) {
		while (fgets(line, sizeof(line), fp) != NULL)
			if ((p = strchr(line, '\n')) != NULL) {
				*p = '\0';
				if (line[0] == '#')
					continue;
				if (strcmp(line, name) == 0) {
					found = 1;
					break;
				}
			}
		(void) fclose(fp);
	}
	return (found);
}

/*
 * Terminate login as previous user, if any, resetting state;
 * used when USER command is given or login fails.
 */
static void
end_login(void)
{
	sigprocmask (SIG_BLOCK, &allsigs, NULL);
	if (logged_in) {
		ftpdlogwtmp(ttyline, "", "");
		if (doutmp)
			ftpd_logout(utmp.ut_line);
	}
	reply(530, "Please reconnect to work as another user");
	_exit(0);
}

enum auth_ret
pass(char *passwd)
{
	int authok;
	unsigned int flags;
	FILE *fp;
	static char homedir[MAXPATHLEN];
	char *motd, *dir, rootdir[MAXPATHLEN];
	size_t sz_pw_dir;

	if (logged_in || askpasswd == 0) {
		reply(503, "Login with USER first.");
		return (AUTH_FAILED);
	}
	askpasswd = 0;
	if (!guest) {		/* "ftp" is only account allowed no password */
		authok = 0;
		if (pw == NULL || pw->pw_passwd[0] == '\0') {
			useconds_t us;

			/* Sleep between 1 and 3 seconds to emulate a crypt. */
			us = arc4random_uniform(3000000);
			usleep(us);
			if (as != NULL) {
				auth_close(as);
				as = NULL;
			}
		} else {
			authok = auth_userresponse(as, passwd, 0);
			as = NULL;
		}
		if (authok == 0) {
			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);
				kill_slave("repeated login failures");
				_exit(0);
			}
			return (AUTH_FAILED);
		}
	} else if (lc != NULL) {
		/* Save anonymous' password. */
		if (guestpw != NULL)
			free(guestpw);
		guestpw = strdup(passwd);
		if (guestpw == NULL) {
			kill_slave("out of mem");
			fatal("Out of memory.");
		}

		authok = auth_approval(as, lc, pw->pw_name, "ftp");
		auth_close(as);
		as = NULL;
		if (authok == 0) {
			syslog(LOG_INFO|LOG_AUTH,
			    "FTP LOGIN FAILED (HOST) as %s: approval failure.",
			    pw->pw_name);
			reply(530, "Approval failure.");
			kill_slave("approval failure");
			_exit(0);
		}
	} else {
		syslog(LOG_INFO|LOG_AUTH,
		    "FTP LOGIN CLASS %s MISSING for %s: approval failure.",
		    pw->pw_class, pw->pw_name);
		reply(530, "Permission denied.");
		kill_slave("permission denied");
		_exit(0);
	}

	if (monitor_post_auth() == 1) {
		/* Post-auth monitor process */
		logged_in = 1;
		return (AUTH_MONITOR);
	}

	login_attempts = 0;		/* this time successful */
	/* set umask via setusercontext() unless -u flag was given. */
	flags = LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES;
	if (umaskchange)
		flags |= LOGIN_SETUMASK;
	else
		(void) umask(defumask);
	if (setusercontext(lc, pw, (uid_t)0, flags) != 0) {
		perror_reply(451, "Local resource failure: setusercontext");
		syslog(LOG_NOTICE, "setusercontext: %m");
		dologout(1);
		/* NOTREACHED */
	}

	/* open wtmp before chroot */
	ftpdlogwtmp(ttyline, pw->pw_name, remotehost);

	/* open utmp before chroot */
	if (doutmp) {
		memset((void *)&utmp, 0, sizeof(utmp));
		(void)time(&utmp.ut_time);
		(void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name));
		(void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host));
		(void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line));
		ftpd_login(&utmp);
	}

	/* open stats file before chroot */
	if (guest && (stats == 1) && (statfd < 0))
		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
			stats = 0;

	logged_in = 1;

	if ((dir = login_getcapstr(lc, "ftp-dir", NULL, NULL))) {
		char *newdir;

		newdir = copy_dir(dir, pw);
		if (newdir == NULL) {
			perror_reply(421, "Local resource failure: malloc");
			dologout(1);
			/* NOTREACHED */
		}
		pw->pw_dir = newdir;
		pw = sgetpwnam(NULL, pw);
		free(dir);
		free(newdir);
	}

	/* make sure pw->pw_dir is big enough to hold "/" */
	sz_pw_dir = strlen(pw->pw_dir) + 1;
	if (sz_pw_dir < 2) {
		pw->pw_dir = "/";
		pw = sgetpwnam(NULL, pw);
		sz_pw_dir = 2;
	}

	if (guest || dochroot) {
		if (multihome && guest) {
			struct stat ts;

			/* Compute root directory. */
			snprintf(rootdir, sizeof(rootdir), "%s/%s",
			    pw->pw_dir, dhostname);
			if (stat(rootdir, &ts) < 0) {
				snprintf(rootdir, sizeof(rootdir), "%s/%s",
				    pw->pw_dir, hostname);
			}
		} else
			strlcpy(rootdir, pw->pw_dir, sizeof(rootdir));
	}
	if (guest) {
		/*
		 * We MUST do a chdir() after the chroot. Otherwise
		 * the old current directory will be accessible as "."
		 * outside the new root!
		 */
		if (chroot(rootdir) < 0 || chdir("/") < 0) {
			reply(550, "Can't set guest privileges.");
			goto bad;
		}
		strlcpy(pw->pw_dir, "/", sz_pw_dir);
		if (setenv("HOME", "/", 1) == -1) {
			reply(550, "Can't setup environment.");
			goto bad;
		}
	} else if (dochroot) {
		if (chroot(rootdir) < 0 || chdir("/") < 0) {
			reply(550, "Can't change root.");
			goto bad;
		}
		strlcpy(pw->pw_dir, "/", sz_pw_dir);
		if (setenv("HOME", "/", 1) == -1) {
			reply(550, "Can't setup environment.");
			goto bad;
		}
	} else if (chdir(pw->pw_dir) < 0) {
		if (chdir("/") < 0) {
			reply(530, "User %s: can't change directory to %s.",
			    pw->pw_name, pw->pw_dir);
			goto bad;
		} else
			lreply(230, "No directory! Logging in with home=/");
	}
	if (setegid(pw->pw_gid) < 0 || setgid(pw->pw_gid) < 0) {
		reply(550, "Can't set gid.");
		goto bad;
	}
	if (seteuid(pw->pw_uid) < 0 || setuid(pw->pw_uid) < 0) {
		reply(550, "Can't set uid.");
		goto bad;
	}
	sigprocmask(SIG_UNBLOCK, &allsigs, NULL);

	/*
	 * Set home directory so that use of ~ (tilde) works correctly.
	 */
	if (getcwd(homedir, MAXPATHLEN) != NULL) {
		if (setenv("HOME", homedir, 1) == -1) {
			reply(550, "Can't setup environment.");
			goto bad;
		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -