ftpd.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,298 行 · 第 1/2 页

C
1,298
字号
#ifndef lintstatic	char	*sccsid = "@(#)ftpd.c	4.3	(ULTRIX)	9/4/90";#endif lint/************************************************************************ *									* *			Copyright (c) 1984,1988,1989 by			* *		Digital Equipment Corporation, Maynard, MA		* *			All rights reserved.				* *									* *   This software is furnished under a license and may be used and	* *   copied  only  in accordance with the terms of such license and	* *   with the  inclusion  of  the  above  copyright  notice.   This	* *   software  or  any  other copies thereof may not be provided or	* *   otherwise made available to any other person.  No title to and	* *   ownership of the software is hereby transferred.			* *									* *   This software is  derived  from  software  received  from  the	* *   University    of   California,   Berkeley,   and   from   Bell	* *   Laboratories.  Use, duplication, or disclosure is  subject  to	* *   restrictions  under  license  agreements  with  University  of	* *   California and with AT&T.						* *									* *   The information in this software is subject to change  without	* *   notice  and should not be construed as a commitment by Digital	* *   Equipment Corporation.						* *									* *   Digital assumes no responsibility for the use  or  reliability	* *   of its software on equipment which is not supplied by Digital.	* *									* ************************************************************************//* * Copyright (c) 1985 Regents of the University of California. * All rights reserved.  The Berkeley software License Agreement * specifies the terms and conditions for redistribution. *//*#ifndef lintchar copyright[] ="Copyright (c) 1985 Regents of the University of California.\n\ All rights reserved.\n";#endif not lint#ifndef lintstatic char sccsid[] = "ftpd.c	5.10 (Berkeley) 9/4/87";#endif not lint*//* * Modification History: * * 07-Aug-90	dlong *	Generate better audit records and allow more parameters to *	reply(). * * 29-Mar-90	dlong *	Get kerberos ticket. * * 15-Aug-89	dlong *	Support enhanced security when logging in. * * 05-Nov-88	jsd *	Use sgetpwnam() to avoid security hole * * 09-Jun-88	Mark Parenti *	Changed signal handlers to void. * *//* * FTP server. */#include <sys/param.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/file.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/ftp.h>#include <arpa/inet.h>#include <arpa/telnet.h>#include <varargs.h>#include <stdio.h>#include <signal.h>#include <pwd.h>#include <setjmp.h>#include <netdb.h>#include <errno.h>#include <strings.h>#include <syslog.h>#include <sys/svcinfo.h>#include <sys/audit.h>#include <auth.h>#ifdef	AUTHEN#include <krb.h>#endif	AUTHEN/* * File containing login names * NOT to be used on this machine. * Commonly used to disallow uucp. */#define	FTPUSERS	"/etc/ftpusers"extern	int errno;extern	char *sys_errlist[];extern	char *crypt();extern	char version[];extern	char *home;		/* pointer to home directory for glob */extern	FILE *popen(), *fopen(), *freopen();extern	int  pclose(), fclose();extern	char *getline();extern	char cbuf[];struct	sockaddr_in ctrl_addr;struct	sockaddr_in data_source;struct	sockaddr_in data_dest;struct	sockaddr_in his_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	logging;int	guest;int	wtmp;int	type;int	form;int	stru;			/* avoid C keyword */int	mode;int	usedefault = 1;		/* for data transfers */int	pdata;			/* for passive mode */int	unique;int	transflag;char	tmpline[7];char	hostname[32];char	remotehost[32];/* * 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;void	lostconn();void	myoob();FILE	*getdatasock(), *dataconn();main(argc, argv)	int argc;	char *argv[];{	int addrlen, on = 1;	long pgid;	char *cp;#ifdef AUTHEN	int i;	char namebuf[ANAME_SZ];	char *ptr;	struct svcinfo *svcp;#endif AUTHEN	addrlen = sizeof (his_addr);	if (getpeername(0, &his_addr, &addrlen) < 0) {		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);		exit(1);	}	addrlen = sizeof (ctrl_addr);	if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);		exit(1);	}	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);	debug = 0;#ifdef 43BSD	openlog("ftpd", LOG_PID, LOG_DAEMON);#else	openlog("ftpd", LOG_PID);#endif 43BSD	argc--, argv++;	while (argc > 0 && *argv[0] == '-') {		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {		case 'v':			debug = 1;			break;		case 'd':			debug = 1;			break;		case 'l':			logging = 1;			break;		case 't':			timeout = atoi(++cp);			goto nextopt;			break;		default:			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",			     *cp);			break;		}nextopt:		argc--, argv++;	}/* * Fire up kerberos */#ifdef AUTHEN        if((svcp = getsvc()) == NULL)                {                fputs("Cannot access security type\n", stderr);                }        if(svcp->svcauth.seclevel >= SEC_UPGRADE)                {                for (i = 0 ; svcp->svcpath[SVC_AUTH][i] != SVC_LAST; i++)                        if (svcp->svcpath[SVC_AUTH][i] == SVC_BIND) {                                if(gethostname(namebuf, sizeof(namebuf)) == -1)                                        {                                        fputs("gethostname failure\n", stderr);                                        }			if((ptr = index(namebuf, '.')) != (char *)0)				*ptr = '\0';                        if(krb_svc_init("hesiod", namebuf, (char *)NULL, 0,                                (char *)NULL, "/var/dss/kerberos/tkt/tkt.ftpd")                                        != RET_OK) {                                fputs("Kerberos initialization failure\n", stderr);                                }                        }                }#endif AUTHEN	(void) freopen("/dev/null", "w", stderr);	(void) signal(SIGPIPE, lostconn);	(void) signal(SIGCHLD, SIG_IGN);	if (signal(SIGURG, myoob) < 0) {		syslog(LOG_ERR, "signal: %m");	}	/* 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 SO_OOBINLINE	pgid = getpid();	if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {		syslog(LOG_ERR, "ioctl: %m");	}	dolog(&his_addr);	/* do telnet option negotiation here */	/*	 * Set up default state	 */	logged_in = 0;	data = -1;	type = TYPE_A;	form = FORM_N;	stru = STRU_F;	mode = MODE_S;	tmpline[0] = '\0';	(void) gethostname(hostname, sizeof (hostname));	reply(220, "%s FTP server (%s) ready.",		hostname, version);	for (;;) {		(void) setjmp(errcatch);		(void) yyparse();	}}voidlostconn(){	if (debug)		syslog(LOG_DEBUG, "lost connection");	dologout(-1);}/* * Helper function for sgetpwnam(). */char *sgetsave(s)	char *s;{#ifdef notdef	char *new = strdup(s);#else	char *malloc();	char *new = malloc((unsigned) strlen(s) + 1);#endif		if (new == NULL) {		reply(553, "Local resource failure");		dologout(1);	}#ifndef notdef	(void) strcpy(new, s);#endif	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). */struct passwd *sgetpwnam(name)	char *name;{	static struct passwd save;	register struct passwd *p;	char *sgetsave();	if ((p = getpwnam(name)) == NULL)		return (p);	if (save.pw_name) {		free(save.pw_name);		free(save.pw_passwd);		free(save.pw_comment);		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_comment = sgetsave(p->pw_comment);	save.pw_gecos = sgetsave(p->pw_gecos);	save.pw_dir = sgetsave(p->pw_dir);	save.pw_shell = sgetsave(p->pw_shell);	return (&save);}static char aud_msg[BUFSIZ];pass(passwd)	char *passwd;{	AUTHORIZATION *auth=NULL, *getauthuid();	char *xpasswd;	extern int sec_level;	char tmask[AUD_NPARAM+1];	char *ar[AUD_NPARAM];	int i=0;	if (logged_in || pw == NULL) {		reply(503, "Login with USER first.");		return;	}	tmask[i] = T_LOGIN;	ar[i++] = pw->pw_name;	tmask[i] = T_HOMEDIR;	ar[i++] = pw->pw_dir;	tmask[i] = T_SERVICE;	ar[i++] = "ftpd";	tmask[i] = T_HOSTADDR2;	ar[i++] = (char *) his_addr.sin_addr;	if (!guest) {		/* "ftp" is only account allowed no password */		config_auth();		if(sec_level < SEC_UPGRADE) {			xpasswd = crypt(passwd, pw->pw_passwd);			/* The strcmp does not catch null passwords! */			if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {				reply(530, "Login incorrect.");				goto bad;			}		} else {			if(!checkpass(pw->pw_uid, passwd) || ((auth=getauthuid(pw->pw_uid)) == NULL)) {				reply(530, "Login incorrect.");				goto bad;			}		}	}	setegid(pw->pw_gid);	initgroups(pw->pw_name, pw->pw_gid);	if (chdir(pw->pw_dir)) {		reply(530, "User %s: can't change directory to %s.",			pw->pw_name, pw->pw_dir);		goto bad;	}	/* grab wtmp before chroot */	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);	if (guest && chroot(pw->pw_dir) < 0) {		reply(550, "Can't set guest privileges.");		if (wtmp >= 0) {			(void) close(wtmp);			wtmp = -1;		}		goto bad;	}	if (!guest)		reply(230, "User %s logged in.", pw->pw_name);	else		reply(230, "Guest login ok, access restrictions apply.");	logged_in = 1;	dologin(pw);	if(auth) {		if(audcntl(SET_PROC_ACNTL, (char *) 0, 0, auth->a_audit_control, 0) < 0) {			reply(530, "audcntl error.");			goto bad;		}		if(audcntl(SET_PROC_AMASK, auth->a_audit_mask, SYSCALL_MASK_LEN+TRUSTED_MASK_LEN, 0) < 0) {			reply(530, "audit mask error.");			goto bad;		}		if(audcntl(SETPAID, (char *) 0, 0, 0, auth->a_audit_id) < 0) {			reply(530, "error setting audit ID.");			goto bad;		}	}	if(*aud_msg) {		tmask[i] = T_CHARP;		ar[i++] = aud_msg;	}	tmask[i] = T_RESULT;	ar[i++] = (char *) 0;	tmask[i] = '\0';	audgen(LOGIN, tmask, ar);	seteuid(pw->pw_uid);	home = pw->pw_dir;		/* home dir for globbing */	return;bad:	seteuid(0);	if(*aud_msg) {		tmask[i] = T_CHARP;		ar[i++] = aud_msg;	}	tmask[i] = T_ERROR;	ar[i++] = (char *) 1;	tmask[i] = '\0';	audgen(LOGIN, tmask, ar);	pw = NULL;}retrieve(cmd, name)	char *cmd, *name;{	FILE *fin, *dout;	struct stat st;	int (*closefunc)(), tmp;	if (cmd == 0) {#ifdef notdef		/* no remote command execution -- it's a security hole */		if (*name == '|')			fin = popen(name + 1, "r"), closefunc = pclose;		else#endif			fin = fopen(name, "r"), closefunc = fclose;	} else {		char line[BUFSIZ];		(void) sprintf(line, cmd, name), name = line;		fin = popen(line, "r"), closefunc = pclose;	}	if (fin == NULL) {		if (errno != 0)			reply(550, "%s: %s.", name, sys_errlist[errno]);		return;	}	st.st_size = 0;	if (cmd == 0 &&	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {		reply(550, "%s: not a plain file.", name);		goto done;	}	dout = dataconn(name, st.st_size, "w");	if (dout == NULL)		goto done;	if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {		reply(550, "%s: %s.", name, sys_errlist[errno]);	}	else if (tmp == 0) {		reply(226, "Transfer complete.");	}	(void) fclose(dout);	data = -1;	pdata = -1;done:	(*closefunc)(fin);}store(name, mode)	char *name, *mode;{	FILE *fout, *din;	int (*closefunc)(), dochown = 0, tmp;	char *gunique(), *local;#ifdef notdef	/* no remote command execution -- it's a security hole */	if (name[0] == '|')		fout = popen(&name[1], "w"), closefunc = pclose;	else#endif	{		struct stat st;		local = name;		if (stat(name, &st) < 0) {			dochown++;		}		else if (unique) {			if ((local = gunique(name)) == NULL) {				return;			}			dochown++;		}		fout = fopen(local, mode), closefunc = fclose;	}	if (fout == NULL) {		reply(553, "%s: %s.", local, sys_errlist[errno]);		return;	}	din = dataconn(local, (off_t)-1, "r");	if (din == NULL)		goto done;	if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {		reply(552, "%s: %s.", local, sys_errlist[errno]);	}	else if (tmp == 0 && !unique) {		reply(226, "Transfer complete.");	}	else if (tmp == 0 && unique) {		reply(226, "Transfer complete (unique file name:%s).", local);	}	(void) fclose(din);	data = -1;	pdata = -1;done:	if (dochown)		(void) chown(local, pw->pw_uid, -1);	(*closefunc)(fout);}FILE *getdatasock(mode)	char *mode;{	int s, on = 1;	if (data >= 0)		return (fdopen(data, mode));	s = socket(AF_INET, SOCK_STREAM, 0);	if (s < 0)		return (NULL);	seteuid(0);	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)		goto bad;	/* anchor socket to avoid multi-homing problems */	data_source.sin_family = AF_INET;	data_source.sin_addr = ctrl_addr.sin_addr;	if (bind(s, &data_source, sizeof (data_source)) < 0)		goto bad;	seteuid(pw->pw_uid);	return (fdopen(s, mode));bad:	seteuid(pw->pw_uid);	(void) close(s);	return (NULL);}FILE *dataconn(name, size, mode)	char *name;	off_t size;	char *mode;{	char sizebuf[32];	FILE *file;	int retry = 0;	if (size >= 0)		(void) sprintf (sizebuf, " (%ld bytes)", size);	else		(void) strcpy(sizebuf, "");	if (pdata > 0) {		struct sockaddr_in from;		int s, fromlen = sizeof(from);		s = accept(pdata, &from, &fromlen);		if (s < 0) {			reply(425, "Can't open data connection.");			(void) close(pdata);			pdata = -1;			return(NULL);		}		(void) close(pdata);		pdata = s;		reply(150, "Openning data connection for %s (%s,%d)%s.",		     name, inet_ntoa(from.sin_addr),		     ntohs(from.sin_port), sizebuf);		return(fdopen(pdata, mode));	}	if (data >= 0) {		reply(125, "Using existing data connection for %s%s.",		    name, sizebuf);		usedefault = 1;		return (fdopen(data, mode));	}	if (usedefault)		data_dest = his_addr;	usedefault = 1;	file = getdatasock(mode);	if (file == NULL) {		reply(425, "Can't create data socket (%s,%d): %s.",		    inet_ntoa(data_source.sin_addr),		    ntohs(data_source.sin_port),		    sys_errlist[errno]);		return (NULL);	}	data = fileno(file);	while (connect(data, &data_dest, sizeof (data_dest)) < 0) {		if (errno == EADDRINUSE && retry < swaitmax) {			sleep((unsigned) swaitint);			retry += swaitint;			continue;		}		reply(425, "Can't build data connection: %s.",		    sys_errlist[errno]);		(void) fclose(file);		data = -1;		return (NULL);	}	reply(150, "Opening data connection for %s (%s,%d)%s.",	    name, inet_ntoa(data_dest.sin_addr),	    ntohs(data_dest.sin_port), sizebuf);	return (file);}/* * Tranfer the contents of "instr" to * "outstr" peer using the appropriate

⌨️ 快捷键说明

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