Skip site navigation (1)Skip section navigation (2)
Date:      20 Feb 2001 14:09:16 +0100
From:      Dag-Erling Smorgrav <des@ofug.org>
To:        audit@freebsd.org
Subject:   MFC ftpd fixes to RELENG_3
Message-ID:  <xzppugdjybn.fsf@flood.ping.uio.no>

next in thread | raw e-mail | index | archive | help
--=-=-=

The attached patch attempts to MFC some of the changes in ftpd between
RELENG_3 and HEAD. I started out with a full diff, then removed all
PAM- and IPv6-related changes and restored the INTERNAL_LS
conditional. The most notable changes (from a RELENG_3 point of view)
are SKEY and Kerberos authentication fixes, addition of the -r
(read-only) option, some anonftp fixes (e.g. chdir("/")), stricter
checking of all commands (including refusing most of them before the
user is logged in), "not a plain file" fixes, long user name support,
and using sendfile(2).

Note that the patch was made with diff -b, so applying it as-is will
result in misindented code. The version I'll commit is properly
indented.

DES
-- 
Dag-Erling Smorgrav - des@ofug.org


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment; filename=ftpd-mfc.diff
Content-Description: patch

Index: ftpcmd.y
===================================================================
RCS file: /home/ncvs/src/libexec/ftpd/ftpcmd.y,v
retrieving revision 1.13.2.1
diff -u -b -r1.13.2.1 ftpcmd.y
--- ftpcmd.y	1999/08/29 15:03:11	1.13.2.1
+++ ftpcmd.y	2001/02/20 13:03:03
@@ -89,6 +89,7 @@
 extern	int usedefault;
 extern  int transflag;
 extern  char tmpline[];
+extern	int readonly;
 
 off_t	restart_point;
 
@@ -127,6 +128,7 @@
 %token	<i> NUMBER
 
 %type	<i> check_login octal_number byte_size
+%type	<i> check_login_ro octal_number byte_size
 %type	<i> struct_code mode_code type_code form_code
 %type	<s> pathstring pathname password username
 
@@ -182,8 +184,9 @@
 			if ($2)
 				passive();
 		}
-	| TYPE SP type_code CRLF
+	| TYPE check_login SP type_code CRLF
 		{
+			if ($2) {
 			switch (cmd_type) {
 
 			case TYPE_A:
@@ -216,10 +219,12 @@
 				UNIMPLEMENTED for NBBY != 8
 #endif /* NBBY == 8 */
 			}
+			}
 		}
-	| STRU SP struct_code CRLF
+	| STRU check_login SP struct_code CRLF
 		{
-			switch ($3) {
+			if ($2) {
+				switch ($4) {
 
 			case STRU_F:
 				reply(200, "STRU F ok.");
@@ -229,9 +234,11 @@
 				reply(504, "Unimplemented STRU type.");
 			}
 		}
-	| MODE SP mode_code CRLF
+		}
+	| MODE check_login SP mode_code CRLF
 		{
-			switch ($3) {
+			if ($2) {
+				switch ($4) {
 
 			case MODE_S:
 				reply(200, "MODE S ok.");
@@ -240,15 +247,20 @@
 			default:
 				reply(502, "Unimplemented MODE type.");
 			}
+			}
 		}
-	| ALLO SP NUMBER CRLF
+	| ALLO check_login SP NUMBER CRLF
 		{
+			if ($2) {
 			reply(202, "ALLO command ignored.");
+			}
 		}
-	| ALLO SP NUMBER SP R SP NUMBER CRLF
+	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
 		{
+			if ($2) {
 			reply(202, "ALLO command ignored.");
 		}
+		}
 	| RETR check_login SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
@@ -256,14 +268,14 @@
 			if ($4 != NULL)
 				free($4);
 		}
-	| STOR check_login SP pathname CRLF
+	| STOR check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				store($4, "w", 0);
 			if ($4 != NULL)
 				free($4);
 		}
-	| APPE check_login SP pathname CRLF
+	| APPE check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				store($4, "a", 0);
@@ -301,18 +313,20 @@
 			if ($4 != NULL)
 				free($4);
 		}
-	| STAT CRLF
+	| STAT check_login CRLF
 		{
+			if ($2) {
 			statcmd();
 		}
-	| DELE check_login SP pathname CRLF
+		}
+	| DELE check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				delete($4);
 			if ($4 != NULL)
 				free($4);
 		}
-	| RNTO check_login SP pathname CRLF
+	| RNTO check_login_ro SP pathname CRLF
 		{
 			if ($2) {
 				if (fromname) {
@@ -325,15 +339,20 @@
 			}
 			free($4);
 		}
-	| ABOR CRLF
+	| ABOR check_login CRLF
 		{
+			if ($2)
 			reply(225, "ABOR command successful.");
 		}
 	| CWD check_login CRLF
 		{
-			if ($2)
+			if ($2) {
+				if (guest)
+					cwd("/");
+				else
 				cwd(pw->pw_dir);
 		}
+		}
 	| CWD check_login SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
@@ -364,14 +383,14 @@
 		{
 			reply(200, "NOOP command successful.");
 		}
-	| MKD check_login SP pathname CRLF
+	| MKD check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				makedir($4);
 			if ($4 != NULL)
 				free($4);
 		}
-	| RMD check_login SP pathname CRLF
+	| RMD check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				removedir($4);
@@ -421,7 +440,7 @@
 				}
 			}
 		}
-	| SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
 		{
 			if ($4 && ($8 != NULL)) {
 				if ($6 > 0777)
@@ -435,35 +454,39 @@
 			if ($8 != NULL)
 				free($8);
 		}
-	| SITE SP IDLE CRLF
+	| SITE SP check_login IDLE CRLF
 		{
+			if ($3)
 			reply(200,
 			    "Current IDLE time limit is %d seconds; max %d",
 				timeout, maxtimeout);
 		}
-	| SITE SP IDLE SP NUMBER CRLF
+	| SITE SP check_login IDLE SP NUMBER CRLF
 		{
-			if ($5 < 30 || $5 > maxtimeout) {
+			if ($3) {
+				if ($6 < 30 || $6 > maxtimeout) {
 				reply(501,
 			"Maximum IDLE time must be between 30 and %d seconds",
 				    maxtimeout);
 			} else {
-				timeout = $5;
+					timeout = $6;
 				(void) alarm((unsigned) timeout);
 				reply(200,
 				    "Maximum IDLE time set to %d seconds",
 				    timeout);
 			}
 		}
-	| STOU check_login SP pathname CRLF
+		}
+	| STOU check_login_ro SP pathname CRLF
 		{
 			if ($2 && $4 != NULL)
 				store($4, "w", 1);
 			if ($4 != NULL)
 				free($4);
 		}
-	| SYST CRLF
+	| SYST check_login CRLF
 		{
+			if ($2)
 #ifdef unix
 #ifdef BSD
 			reply(215, "UNIX Type: L%d Version: BSD-%d",
@@ -533,7 +556,7 @@
 		}
 	;
 rcmd
-	: RNFR check_login SP pathname CRLF
+	: RNFR check_login_ro SP pathname CRLF
 		{
 			char *renamefrom();
 
@@ -545,13 +568,16 @@
 				}
 			}
 		}
-	| REST SP byte_size CRLF
+	| REST check_login SP byte_size CRLF
 		{
+			if ($2) {
 			fromname = (char *) 0;
-			restart_point = $3;	/* XXX $3 is only "int" */
-			reply(350, "Restarting at %qd. %s", restart_point,
+				restart_point = $4;  /* XXX $4 is only "int" */
+				reply(350, "Restarting at %qd. %s",
+				    restart_point,
 			    "Send STORE or RETRIEVE to initiate transfer.");
 		}
+		}
 	;
 
 username
@@ -735,12 +761,19 @@
 check_login
 	: /* empty */
 		{
-			if (logged_in)
-				$$ = 1;
-			else {
-				reply(530, "Please login with USER and PASS.");
+		$$ = check_login1();
+		}
+	;
+
+check_login_ro
+	: /* empty */
+		{
+		if (readonly) {
+			reply(550, "Permission denied.");
 				$$ = 0;
 			}
+		else
+			$$ = check_login1();
 		}
 	;
 
@@ -1254,7 +1287,9 @@
 	case TYPE_L:
 	case TYPE_I: {
 		struct stat stbuf;
-		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
+		if (stat(filename, &stbuf) < 0)
+			perror_reply(550, filename);
+		else if (!S_ISREG(stbuf.st_mode))
 			reply(550, "%s: not a plain file.", filename);
 		else
 			reply(213, "%qu", stbuf.st_size);
@@ -1269,7 +1304,11 @@
 			perror_reply(550, filename);
 			return;
 		}
-		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
+		if (fstat(fileno(fin), &stbuf) < 0) {
+			perror_reply(550, filename);
+			(void) fclose(fin);
+			return;
+		} else if (!S_ISREG(stbuf.st_mode)) {
 			reply(550, "%s: not a plain file.", filename);
 			(void) fclose(fin);
 			return;
@@ -1289,3 +1328,15 @@
 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
 	}
 }
+
+static int
+check_login1()
+{
+	if (logged_in)
+		return 1;
+	else {
+		reply(530, "Please login with USER and PASS.");
+		return 0;
+	}
+}
+
Index: ftpd.8
===================================================================
RCS file: /home/ncvs/src/libexec/ftpd/ftpd.8,v
retrieving revision 1.25.2.3
diff -u -b -r1.25.2.3 ftpd.8
--- ftpd.8	1999/08/29 15:03:12	1.25.2.3
+++ ftpd.8	2001/02/19 21:46:42
@@ -32,7 +32,7 @@
 .\"     @(#)ftpd.8	8.2 (Berkeley) 4/19/94
 .\" $FreeBSD: src/libexec/ftpd/ftpd.8,v 1.25.2.3 1999/08/29 15:03:12 peter Exp $
 .\"
-.Dd April 19, 1994
+.Dd February 19, 2000
 .Dt FTPD 8
 .Os BSD 4.2
 .Sh NAME
@@ -48,6 +48,7 @@
 .Op Fl R
 .Op Fl S
 .Op Fl U
+.Op Fl r
 .Op Fl T Ar maxtimeout
 .Op Fl t Ar timeout
 .Op Fl a Ar address
@@ -84,8 +85,8 @@
 With this option set,
 .Nm
 will detach and become a daemon, accepting connections on the FTP port and
-forking children processes to handle them. This is lower overhead than
-starting
+forking children processes to handle them.
+This is lower overhead than starting
 .Nm
 from
 .Xr inetd 8
@@ -103,7 +104,7 @@
 .It Fl S
 With this option set,
 .Nm
-logs all anonymous transfers to the file
+logs all anonymous file downloads to the file
 .Pa /var/log/ftpd
 when this file exists.
 .It Fl U
@@ -137,6 +138,9 @@
 .Ar file .
 .It Fl A
 Allow only anonymous ftp access.
+.It Fl r
+Put server in read-only mode.
+All commands which may modify the local filesystem are disabled.
 .El
 .Pp
 The file
@@ -162,20 +166,23 @@
 in the anonymous user's case.
 .Pp
 The ftp server currently supports the following ftp requests.
-The case of the requests is ignored.
+The case of the requests is ignored.  Requests marked [RW] are
+disabled if 
+.Fl r
+is specified.
 .Bl -column "Request" -offset indent
 .It Sy Request Ta Sy "Description"
 .It ABOR Ta "abort previous command"
 .It ACCT Ta "specify account (ignored)"
 .It ALLO Ta "allocate storage (vacuously)"
-.It APPE Ta "append to a file"
+.It APPE Ta "append to a file [RW]"
 .It CDUP Ta "change to parent of current working directory"
 .It CWD Ta "change working directory"
-.It DELE Ta "delete a file"
+.It DELE Ta "delete a file [RW]"
 .It HELP Ta "give help information"
 .It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
-.It MKD Ta "make a directory"
 .It MDTM Ta "show last modification time of file"
+.It MKD Ta "make a directory [RW]"
 .It MODE Ta "specify data transfer" Em mode
 .It NLST Ta "give name list of files in directory"
 .It NOOP Ta "do nothing"
@@ -186,23 +193,23 @@
 .It QUIT Ta "terminate session"
 .It REST Ta "restart incomplete transfer"
 .It RETR Ta "retrieve a file"
-.It RMD Ta "remove a directory"
-.It RNFR Ta "specify rename-from file name"
-.It RNTO Ta "specify rename-to file name"
+.It RMD Ta "remove a directory [RW]"
+.It RNFR Ta "specify rename-from file name [RW]"
+.It RNTO Ta "specify rename-to file name [RW]"
 .It SITE Ta "non-standard commands (see next section)"
 .It SIZE Ta "return size of file"
 .It STAT Ta "return status of server"
-.It STOR Ta "store a file"
-.It STOU Ta "store a file with a unique name"
+.It STOR Ta "store a file [RW]"
+.It STOU Ta "store a file with a unique name [RW]"
 .It STRU Ta "specify data transfer" Em structure
 .It SYST Ta "show operating system type of server system"
 .It TYPE Ta "specify data transfer" Em type
 .It USER Ta "specify user name"
 .It XCUP Ta "change to parent of current working directory (deprecated)"
 .It XCWD Ta "change working directory (deprecated)"
-.It XMKD Ta "make a directory (deprecated)"
+.It XMKD Ta "make a directory (deprecated) [RW]"
 .It XPWD Ta "print the current working directory (deprecated)"
-.It XRMD Ta "remove a directory (deprecated)"
+.It XRMD Ta "remove a directory (deprecated) [RW]"
 .El
 .Pp
 The following non-standard or
@@ -215,7 +222,7 @@
 .It Sy Request Ta Sy Description
 .It UMASK Ta change umask, e.g. ``SITE UMASK 002''
 .It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60''
-.It CHMOD Ta "change mode of a file, e.g. ``SITE CHMOD 755 filename''"
+.It CHMOD Ta "change mode of a file [RW], e.g. ``SITE CHMOD 755 filename''"
 .It HELP Ta give help information
 .El
 .Pp
@@ -244,7 +251,7 @@
 .Dq Li \&*?[]{}~ .
 .Pp
 .Nm Ftpd
-authenticates users according to five rules. 
+authenticates users according to six rules. 
 .Pp
 .Bl -enum -offset indent
 .It
@@ -253,13 +260,15 @@
 In this case a password must be provided by the client before any
 file operations may be performed.
 If the user has an S/Key key, the response from a successful USER
-command will include an S/Key challenge. The client may choose to respond
-with a PASS command giving either a standard password or an S/Key
-one-time password. The server will automatically determine which type of
-password it has been given and attempt to authenticate accordingly. See
+command will include an S/Key challenge.
+The client may choose to respond with a PASS command giving either
+a standard password or an S/Key one-time password.
+The server will automatically determine which type of
+password it has been given and attempt to authenticate accordingly.
+See
 .Xr key 1
-for more information on S/Key authentication. S/Key is a Trademark of
-Bellcore.
+for more information on S/Key authentication.
+S/Key is a Trademark of Bellcore.
 .It
 The login name must not appear in the file
 .Pa /etc/ftpusers .
@@ -462,3 +471,4 @@
 .Nm
 command appeared in
 .Bx 4.2 .
+IPv6 support was added in WIDE Hydrangea IPv6 stack kit.
Index: ftpd.c
===================================================================
RCS file: /home/ncvs/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.52.2.3
diff -u -b -r1.52.2.3 ftpd.c
--- ftpd.c	1999/10/25 08:31:50	1.52.2.3
+++ ftpd.c	2001/02/19 21:40:38
@@ -51,11 +51,12 @@
  * FTP server.
  */
 #include <sys/param.h>
-#include <sys/stat.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/wait.h>
-#include <sys/mman.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -142,6 +143,7 @@
 int	mode;
 int	usedefault = 1;		/* for data transfers */
 int	pdata = -1;		/* for passive mode */
+int	readonly=0;		/* Server is in readonly mode.	*/
 sig_atomic_t transflag;
 off_t	file_size;
 off_t	byte_count;
@@ -206,7 +208,6 @@
 
 #ifdef SKEY
 int	pwok = 0;
-char	addr_string[20];	/* XXX */
 #endif
 
 #define LOGCMD(cmd, file) \
@@ -287,7 +288,7 @@
 
 
 	bind_address.s_addr = htonl(INADDR_ANY);
-	while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) {
+	while ((ch = getopt(argc, argv, "AdlDSURrt:T:u:va:p:")) != -1) {
 		switch (ch) {
 		case 'D':
 			daemon_mode++;
@@ -301,6 +302,10 @@
 			logging++;	/* > 1 == extra logging */
 			break;
 
+		case 'r':
+			readonly = 1;
+			break;
+
 		case 'R':
 			paranoid = 0;
 			break;
@@ -401,7 +406,7 @@
 		}
 		if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
 		    (char *)&on, sizeof(on)) < 0)
-			syslog(LOG_ERR, "control setsockopt: %m");;
+			syslog(LOG_ERR, "control setsockopt: %m");
 		server_addr.sin_family = AF_INET;
 		server_addr.sin_addr = bind_address;
 		server_addr.sin_port = sv->s_port;
@@ -464,9 +469,6 @@
 	if (signal(SIGURG, myoob) == SIG_ERR)
 		syslog(LOG_ERR, "signal: %m");
 
-#ifdef SKEY
-	strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string));
-#endif
 	addrlen = sizeof(ctrl_addr);
 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
@@ -754,7 +756,7 @@
 
 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 */
+static char curname[MAXLOGNAME];	/* current USER name */
 
 /*
  * USER command.
@@ -831,7 +833,7 @@
 	if (logging)
 		strncpy(curname, name, sizeof(curname)-1);
 #ifdef SKEY
-	pwok = skeyaccess(name, NULL, remotehost, addr_string);
+	pwok = skeyaccess(name, NULL, remotehost, remotehost);
 	reply(331, "%s", skey_challenge(name, pw, pwok));
 #else
 	reply(331, "Password required for %s.", name);
@@ -944,11 +946,15 @@
 			goto skip;
 #endif
 #ifdef SKEY
-		rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok),
-			      pw->pw_passwd);
-		pwok = 0;
+		rval = 1;
+		if (pwok)
+			rval = strcmp(pw->pw_passwd,
+			    crypt(passwd, pw->pw_passwd));
+		if (rval)
+			rval = strcmp(pw->pw_passwd,
+			    skey_crypt(passwd, pw->pw_passwd, pw, pwok));
 #else
-		rval = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
+		rval = strcmp(pw->pw_passwd, crypt(passwd, pw->pw_passwd));
 #endif
 		/* The strcmp does not catch null passwords! */
 		if (*pw->pw_passwd == '\0' ||
@@ -976,6 +982,9 @@
 			return;
 		}
 	}
+#ifdef SKEY
+	pwok = 0;
+#endif
 	login_attempts = 0;		/* this time successful */
 	if (setegid((gid_t)pw->pw_gid) < 0) {
 		reply(550, "Can't set gid.");
@@ -1089,14 +1098,14 @@
 		if (thishost != firsthost)
 			snprintf(proctitle, sizeof(proctitle),
 				 "%s: anonymous(%s)/%.*s", remotehost, hostname,
-				 sizeof(proctitle) - sizeof(remotehost) -
-				 sizeof(": anonymous/"), passwd);
+				 (int)(sizeof(proctitle) - sizeof(remotehost) -
+				 sizeof(": anonymous/")), passwd);
 		else
 #endif
 			snprintf(proctitle, sizeof(proctitle),
 				 "%s: anonymous/%.*s", remotehost,
-				 sizeof(proctitle) - sizeof(remotehost) -
-				 sizeof(": anonymous/"), passwd);
+				 (int)(sizeof(proctitle) - sizeof(remotehost) -
+				 sizeof(": anonymous/")), passwd);
 		setproctitle("%s", proctitle);
 #endif /* SETPROCTITLE */
 		if (logging)
@@ -1282,6 +1291,7 @@
 	if (data >= 0)
 		return (fdopen(data, mode));
 	(void) seteuid((uid_t)0);
+
 	s = socket(AF_INET, SOCK_STREAM, 0);
 	if (s < 0)
 		goto bad;
@@ -1426,9 +1436,10 @@
 	off_t filesize;
 	int isreg;
 {
-	int c, cnt, filefd, netfd;
-	char *buf, *bp;
+	int c, filefd, netfd;
+	char *buf;
 	size_t len;
+	off_t cnt;
 
 	transflag++;
 	if (setjmp(urgcatch)) {
@@ -1465,27 +1476,28 @@
 		netfd = fileno(outstr);
 		filefd = fileno(instr);
 
-		if (isreg && filesize < (off_t)16 * 1024 * 1024) {
-			buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
-				   (off_t)0);
-			if (buf == MAP_FAILED) {
-				syslog(LOG_WARNING, "mmap(%lu): %m",
-				       (unsigned long)filesize);
-				goto oldway;
-			}
-			bp = buf;
+		if (isreg) {
+
+			off_t offset;
+			int err;
+
 			len = filesize;
-			do {
-				cnt = write(netfd, bp, len);
+			err = cnt = offset = 0;
+
+			while (err != -1 && cnt < filesize) {
+				err = sendfile(filefd, netfd, offset, len,
+					(struct sf_hdtr *) NULL, &cnt, 0);
+				offset += cnt;
 				len -= cnt;
-				bp += cnt;
-				if (cnt > 0) byte_count += cnt;
-			} while(cnt > 0 && len > 0);
 
-			transflag = 0;
-			munmap(buf, (size_t)filesize);
-			if (cnt < 0)
+				if (err == -1) {
+					if (!cnt)
+						goto oldway;
+
 				goto data_err;
+				}
+			}
+
 			reply(226, "Transfer complete.");
 			return;
 		}

--=-=-=--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?xzppugdjybn.fsf>