From owner-freebsd-audit Tue Feb 20 5: 9:39 2001 Delivered-To: freebsd-audit@freebsd.org Received: from flood.ping.uio.no (flood.ping.uio.no [129.240.78.31]) by hub.freebsd.org (Postfix) with ESMTP id 5D72637B401 for ; Tue, 20 Feb 2001 05:09:18 -0800 (PST) (envelope-from des@ofug.org) Received: (from des@localhost) by flood.ping.uio.no (8.9.3/8.9.3) id OAA34149; Tue, 20 Feb 2001 14:09:17 +0100 (CET) (envelope-from des@ofug.org) X-URL: http://www.ofug.org/~des/ X-Disclaimer: The views expressed in this message do not necessarily coincide with those of any organisation or company with which I am or have been affiliated. To: audit@freebsd.org Subject: MFC ftpd fixes to RELENG_3 From: Dag-Erling Smorgrav Date: 20 Feb 2001 14:09:16 +0100 Message-ID: Lines: 20 User-Agent: Gnus/5.0802 (Gnus v5.8.2) Emacs/20.4 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Sender: owner-freebsd-audit@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG --=-=-= 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 NUMBER %type check_login octal_number byte_size +%type check_login_ro octal_number byte_size %type struct_code mode_code type_code form_code %type 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 -#include #include +#include #include +#include +#include #include -#include #include #include @@ -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