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

📄 ftpd.c

📁 伯克利大学的一个ftp协议的实现源代码,包括客户端和服务器端.
💻 C
📖 第 1 页 / 共 4 页
字号:
	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)	const char *name;{	static struct passwd save;	struct passwd *p;	if ((p = getpwnam(name)) == NULL)		return (p);#ifdef USE_SHADOW	if ((spw = getspnam(name)) != NULL)		p->pw_passwd = spw->sp_pwdp;	endspent();  /* ? */#endif	if (save.pw_name) {		free(save.pw_name);		memset(save.pw_passwd, 0, strlen(save.pw_passwd));		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[16];	/* 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;{	const char *cp, *shell;	if (logged_in) {		if (guest) {			reply(530, "Can't change user from guest login.");			return;		} else if (dochroot) {			reply(530, "Can't change user from chroot user.");			return;		}		end_login();	}	guest = 0;	if (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) {			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 (anon_only && !checkuser(_PATH_FTPCHROOT, name)) {		reply(530, "Sorry, only anonymous ftp allowed.");		return;	}	if ((pw = sgetpwnam(name))!=NULL) {		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(_PATH_FTPUSERS, 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);		curname[sizeof(curname)-1] = '\0';	}#ifdef SKEY	if (!skey_haskey(name)) {		char *myskey, *skey_keyinfo __P((char *name));		myskey = skey_keyinfo(name);		reply(331, "Password [ %s ] for %s required.",		    myskey ? myskey : "error getting challenge", name);	} else#endif		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 intcheckuser(fname, name)	const char *fname;	const char *name;{	FILE *fd;	int found = 0;	char *p, line[BUFSIZ];	if ((fd = fopen(fname, "r")) != NULL) {		while (fgets(line, sizeof(line), fd) != NULL)			if ((p = strchr(line, '\n')) != NULL) {				*p = '\0';				if (line[0] == '#')					continue;				if (strcmp(line, 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(){	sigset_t allsigs;	sigfillset (&allsigs);	sigprocmask (SIG_BLOCK, &allsigs, NULL);	(void) seteuid((uid_t)0);	if (logged_in) {		logwtmp(ttyline, "", "");		if (doutmp)			logout(utmp.ut_line);	}	pw = NULL;	logged_in = 0;	guest = 0;	dochroot = 0;}voidpass(passwd)	char *passwd;{	int rval;	FILE *fd;	static char homedir[MAXPATHLEN];	char rootdir[MAXPATHLEN];	sigset_t allsigs;	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) {			rval = 1;	/* failure below */			goto skip;		}#if defined(KERBEROS)		rval = klogin(pw, "", hostname, passwd);		if (rval == 0)			goto skip;#endif#ifdef SKEY		if (skey_haskey(pw->pw_name) == 0 &&		   (skey_passcheck(pw->pw_name, passwd) != -1)) {			rval = 0;			goto skip;		}#endif		/* the strcmp does not catch null passwords! */		if (pw == NULL || *pw->pw_passwd == '\0' ||		    strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {			rval = 1;	 /* failure */			goto skip;		}		rval = 0;skip:		/*		 * If rval == 1, the user failed the authentication check		 * above.  If rval == 0, either Kerberos or local authentication		 * succeeded.		 */		if (rval) {			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;		}	} else {		/* Save anonymous' password. */		guestpw = strdup(passwd);		if (guestpw == (char *)NULL)			fatal("Out of memory");	}	login_attempts = 0;		/* this time successful */#ifdef USE_SHADOW	switch (isexpired(spw)) {	  case 0: /* success */		break;	  case 1:		syslog(LOG_NOTICE, "expired password from %s, %s",		       remotehost, pw->pw_name);		reply(530, "Please change your password and try again.");		return;	  case 2:       		syslog(LOG_NOTICE, "inactive login from %s, %s",		       remotehost, pw->pw_name);		reply(530, "Login inactive -- contact administrator.");		return;	  case 3:		syslog(LOG_NOTICE, "expired login from %s, %s",		       remotehost, pw->pw_name);		reply(530, "Account expired -- contact administrator.");		return;	}#endif	if (setegid((gid_t)pw->pw_gid) < 0) {		reply(550, "Can't set gid.");		return;	}	(void) initgroups(pw->pw_name, pw->pw_gid);	/* open wtmp before chroot */	logwtmp(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));		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;	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);	if (guest || dochroot) {		if (multihome) {			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			strcpy (rootdir, pw->pw_dir);	}	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;		}		strcpy(pw->pw_dir, "/");		setenv("HOME", "/", 1);	} else if (dochroot) {		if (chroot(rootdir) < 0 || chdir("/") < 0) {			reply(550, "Can't change root.");			goto bad;		}		strcpy(pw->pw_dir, "/");		setenv("HOME", "/", 1);	} 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 (seteuid((uid_t)pw->pw_uid) < 0) {		reply(550, "Can't set uid.");		goto bad;	}	sigfillset(&allsigs);	sigprocmask(SIG_UNBLOCK,&allsigs,NULL);	/*	 * Set home directory so that use of ~ (tilde) works correctly.	 */	if (getcwd(homedir, MAXPATHLEN) != NULL)		setenv("HOME", homedir, 1);	/*	 * Display a login message, if it exists.	 * N.B. reply(230,) must follow the message.	 */	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {		char *cp, line[LINE_MAX];		while (fgets(line, sizeof(line), fd) != NULL) {			if ((cp = strchr(line, '\n')) != NULL)				*cp = '\0';			lreply(230, "%s", line);		}		(void) fflush(stdout);		(void) fclose(fd);	}	if (guest) {		if (ident != NULL)			free(ident);		ident = strdup(passwd);		if (ident == (char *)NULL)			fatal("Ran out of memory.");		reply(230, "Guest login ok, access restrictions apply.");#ifdef HASSETPROCTITLE		snprintf(proctitle, sizeof(proctitle),		    "%s: anonymous/%.*s", remotehost,		    sizeof(proctitle) - sizeof(remotehost) -		    sizeof(": anonymous/"), passwd);		setproctitle(proctitle);#endif /* HASSETPROCTITLE */		if (logging)			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",			    remotehost, passwd);	} else {		reply(230, "User %s logged in.", pw->pw_name);#ifdef HASSETPROCTITLE		snprintf(proctitle, sizeof(proctitle),		    "%s: %s", remotehost, pw->pw_name);		setproctitle(proctitle);#endif /* HASSETPROCTITLE */		if (logging)			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",			    remotehost, pw->pw_name);	}	(void) umask(defumask);	return;bad:	/* Forget all about it... */	end_login();}voidretrieve(cmd, name)	const char *cmd, *name;{	FILE *fin, *dout;	struct stat st;	int (*closefunc) __P((FILE *));	time_t start;	if (cmd == 0) {		fin = fopen(name, "r"), closefunc = fclose;		st.st_size = 0;	} else {		char line[BUFSIZ];		(void) snprintf(line, sizeof(line), cmd, name);		name = line;		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;		st.st_size = -1;		st.st_blksize = BUFSIZ;	}	if (fin == NULL) {		if (errno != 0) {			perror_reply(550, name);			if (cmd == 0) {				LOGCMD("get", name);			}		}		return;	}	byte_count = -1;	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {		reply(550, "%s: not a plain file.", name);		goto done;	}	if (restart_point) {		if (type == TYPE_A) {			off_t i, n;			int c;			n = restart_point;			i = 0;			while (i++ < n) {				if ((c=getc(fin)) == EOF) {					perror_reply(550, name);					goto done;				}				if (c == '\n')					i++;			}		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {			perror_reply(550, name);			goto done;		}	}	dout = dataconn(name, st.st_size, "w");	if (dout == NULL)		goto done;	time(&start);	send_data(fin, dout, st.st_blksize, st.st_size,		  (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)));	if ((cmd == 0) && stats)		logxfer(name, st.st_size, start);	(void) fclose(dout);	data = -1;	pdata = -1;done:	if (cmd == 0)		LOGBYTES("get", name, byte_count);	(*closefunc)(fin);}voidstore(name, mode, unique)	const char *name, *mode;	int unique;{	FILE *fout, *din;	int (*closefunc) __P((FILE *));	struct stat st;	int fd;	if (unique && stat(name, &st) == 0) {		char *nam;		fd = guniquefd(name, &nam);		if (fd == -1) {			LOGCMD(*mode == 'w' ? "put" : "append", name);			return;		}		name = nam;		if (restart_point)			mode = "r+";		fout = fdopen(fd, mode);	} else		fout = fopen(name, mode);	closefunc = fclose;	if (fout == NULL) {		perror_reply(553, name);		LOGCMD(*mode == 'w' ? "put" : "append", name);		return;	}	byte_count = -1;	if (restart_point) {		if (type == TYPE_A) {			off_t i, n;			int c;			n = restart_point;			i = 0;			while (i++ < n) {				if ((c=getc(fout)) == EOF) {					perror_reply(550, name);					goto done;				}				if (c == '\n')					i++;			}			/*			 * We must do this seek to "current" position			 * because we are changing from reading to			 * writing.			 */			if (fseek(fout, 0L, L_INCR) < 0) {				perror_reply(550, name);				goto done;			}		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {			perror_reply(550, name);			goto done;		}	}	din = dataconn(name, (off_t)-1, "r");	if (din == NULL)		goto done;	if (receive_data(din, fout) == 0) {		if (unique)			reply(226, "Transfer complete (unique file name:%s).",			    name);		else			reply(226, "Transfer complete.");

⌨️ 快捷键说明

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