BSD 4_3_Reno release
[unix-history] / usr / src / libexec / ftpd / ftpd.c
index a08be0a..1c5f751 100644 (file)
@@ -1,28 +1,30 @@
 /*
 /*
- * Copyright (c) 1985, 1988 Regents of the University of California.
+ * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
  * All rights reserved.
  *
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement:  ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
 #ifndef lint
 char copyright[] =
  */
 
 #ifndef lint
 char copyright[] =
-"@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
+"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)ftpd.c     5.27    (Berkeley) %G%";
+static char sccsid[] = "@(#)ftpd.c     5.37 (Berkeley) 6/27/90";
 #endif /* not lint */
 
 /*
 #endif /* not lint */
 
 /*
@@ -37,6 +39,8 @@ static char sccsid[] = "@(#)ftpd.c    5.27    (Berkeley) %G%";
 #include <sys/dir.h>
 
 #include <netinet/in.h>
 #include <sys/dir.h>
 
 #include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
 
 #define        FTP_NAMES
 #include <arpa/ftp.h>
 
 #define        FTP_NAMES
 #include <arpa/ftp.h>
@@ -50,20 +54,17 @@ static char sccsid[] = "@(#)ftpd.c  5.27    (Berkeley) %G%";
 #include <setjmp.h>
 #include <netdb.h>
 #include <errno.h>
 #include <setjmp.h>
 #include <netdb.h>
 #include <errno.h>
-#include <strings.h>
+#include <string.h>
 #include <syslog.h>
 #include <varargs.h>
 #include <syslog.h>
 #include <varargs.h>
+#include "pathnames.h"
 
 /*
  * File containing login names
  * NOT to be used on this machine.
  * Commonly used to disallow uucp.
  */
 
 /*
  * File containing login names
  * NOT to be used on this machine.
  * Commonly used to disallow uucp.
  */
-#define        FTPUSERS        "/etc/ftpusers"
-
 extern int errno;
 extern int errno;
-extern char *sys_errlist[];
-extern int sys_nerr;
 extern char *crypt();
 extern char version[];
 extern char *home;             /* pointer to home directory for glob */
 extern char *crypt();
 extern char version[];
 extern char *home;             /* pointer to home directory for glob */
@@ -132,7 +133,7 @@ main(argc, argv, envp)
        char *argv[];
        char **envp;
 {
        char *argv[];
        char **envp;
 {
-       int addrlen, on = 1;
+       int addrlen, on = 1, tos;
        char *cp;
 
        addrlen = sizeof (his_addr);
        char *cp;
 
        addrlen = sizeof (his_addr);
@@ -145,6 +146,11 @@ main(argc, argv, envp)
                syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
                exit(1);
        }
                syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
                exit(1);
        }
+#ifdef IP_TOS
+       tos = IPTOS_LOWDELAY;
+       if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
        data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
        debug = 0;
        openlog("ftpd", LOG_PID, LOG_DAEMON);
        data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
        debug = 0;
        openlog("ftpd", LOG_PID, LOG_DAEMON);
@@ -207,18 +213,18 @@ main(argc, argv, envp)
 nextopt:
                argc--, argv++;
        }
 nextopt:
                argc--, argv++;
        }
-       (void) freopen("/dev/null", "w", stderr);
+       (void) freopen(_PATH_DEVNULL, "w", stderr);
        (void) signal(SIGPIPE, lostconn);
        (void) signal(SIGCHLD, SIG_IGN);
        if ((int)signal(SIGURG, myoob) < 0)
                syslog(LOG_ERR, "signal: %m");
 
        (void) signal(SIGPIPE, lostconn);
        (void) signal(SIGCHLD, SIG_IGN);
        if ((int)signal(SIGURG, myoob) < 0)
                syslog(LOG_ERR, "signal: %m");
 
-       /* handle urgent data inline */
-       /* Sequent defines this, but it doesn't work */
+       /* Try to 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
 #ifdef SO_OOBINLINE
        if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
                syslog(LOG_ERR, "setsockopt: %m");
 #endif
+
 #ifdef F_SETOWN
        if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
                syslog(LOG_ERR, "fcntl F_SETOWN: %m");
 #ifdef F_SETOWN
        if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
                syslog(LOG_ERR, "fcntl F_SETOWN: %m");
@@ -306,25 +312,21 @@ int askpasswd;                    /* had user command, ask for passwd */
 
 /*
  * USER command.
 
 /*
  * 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" 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 returned by getusershell() (/etc/shells).
- * Disallow anyone mentioned in the file FTPUSERS
- * to allow people such as root and uucp to be avoided.
+ * 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.
  */
 user(name)
        char *name;
 {
        register char *cp;
  */
 user(name)
        char *name;
 {
        register char *cp;
-       FILE *fd;
        char *shell;
        char *shell;
-       char line[BUFSIZ], *getusershell();
+       char *getusershell();
 
        if (logged_in) {
                if (guest) {
 
        if (logged_in) {
                if (guest) {
@@ -336,7 +338,9 @@ user(name)
 
        guest = 0;
        if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
 
        guest = 0;
        if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
-               if ((pw = sgetpwnam("ftp")) != NULL) {
+               if (checkuser("ftp") || checkuser("anonymous"))
+                       reply(530, "User %s access denied.", name);
+               else if ((pw = sgetpwnam("ftp")) != NULL) {
                        guest = 1;
                        askpasswd = 1;
                        reply(331, "Guest login ok, send ident as password.");
                        guest = 1;
                        askpasswd = 1;
                        reply(331, "Guest login ok, send ident as password.");
@@ -346,12 +350,12 @@ user(name)
        }
        if (pw = sgetpwnam(name)) {
                if ((shell = pw->pw_shell) == NULL || *shell == 0)
        }
        if (pw = sgetpwnam(name)) {
                if ((shell = pw->pw_shell) == NULL || *shell == 0)
-                       shell = "/bin/sh";
+                       shell = _PATH_BSHELL;
                while ((cp = getusershell()) != NULL)
                        if (strcmp(cp, shell) == 0)
                                break;
                endusershell();
                while ((cp = getusershell()) != NULL)
                        if (strcmp(cp, shell) == 0)
                                break;
                endusershell();
-               if (cp == NULL) {
+               if (cp == NULL || checkuser(name)) {
                        reply(530, "User %s access denied.", name);
                        if (logging)
                                syslog(LOG_NOTICE,
                        reply(530, "User %s access denied.", name);
                        if (logging)
                                syslog(LOG_NOTICE,
@@ -360,22 +364,6 @@ user(name)
                        pw = (struct passwd *) NULL;
                        return;
                }
                        pw = (struct passwd *) NULL;
                        return;
                }
-               if ((fd = fopen(FTPUSERS, "r")) != NULL) {
-                   while (fgets(line, sizeof (line), fd) != NULL) {
-                       if ((cp = index(line, '\n')) != NULL)
-                               *cp = '\0';
-                       if (strcmp(line, name) == 0) {
-                               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;
-                       }
-                   }
-               }
-               (void) fclose(fd);
        }
        reply(331, "Password required for %s.", name);
        askpasswd = 1;
        }
        reply(331, "Password required for %s.", name);
        askpasswd = 1;
@@ -387,6 +375,30 @@ user(name)
                sleep((unsigned) login_attempts);
 }
 
                sleep((unsigned) login_attempts);
 }
 
+/*
+ * Check if a user is in the file _PATH_FTPUSERS
+ */
+checkuser(name)
+       char *name;
+{
+       register FILE *fd;
+       register char *p;
+       char line[BUFSIZ];
+
+       if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+               while (fgets(line, sizeof(line), fd) != NULL)
+                       if ((p = index(line, '\n')) != NULL) {
+                               *p = '\0';
+                               if (line[0] == '#')
+                                       continue;
+                               if (strcmp(line, name) == 0)
+                                       return (1);
+                       }
+               (void) fclose(fd);
+       }
+       return (0);
+}
+
 /*
  * Terminate login as previous user, if any, resetting state;
  * used when USER command is given or login fails.
 /*
  * Terminate login as previous user, if any, resetting state;
  * used when USER command is given or login fails.
@@ -620,7 +632,7 @@ FILE *
 getdatasock(mode)
        char *mode;
 {
 getdatasock(mode)
        char *mode;
 {
-       int s, on = 1;
+       int s, on = 1, tries;
 
        if (data >= 0)
                return (fdopen(data, mode));
 
        if (data >= 0)
                return (fdopen(data, mode));
@@ -628,14 +640,26 @@ getdatasock(mode)
        if (s < 0)
                return (NULL);
        (void) seteuid((uid_t)0);
        if (s < 0)
                return (NULL);
        (void) seteuid((uid_t)0);
-       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 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;
                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, (struct sockaddr *)&data_source, sizeof (data_source)) < 0)
-               goto bad;
+       for (tries = 1; ; tries++) {
+               if (bind(s, (struct sockaddr *)&data_source,
+                   sizeof (data_source)) >= 0)
+                       break;
+               if (errno != EADDRINUSE || tries > 10)
+                       goto bad;
+               sleep(tries);
+       }
        (void) seteuid((uid_t)pw->pw_uid);
        (void) seteuid((uid_t)pw->pw_uid);
+#ifdef IP_TOS
+       on = IPTOS_THROUGHPUT;
+       if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
        return (fdopen(s, mode));
 bad:
        (void) seteuid((uid_t)pw->pw_uid);
        return (fdopen(s, mode));
 bad:
        (void) seteuid((uid_t)pw->pw_uid);
@@ -651,7 +675,7 @@ dataconn(name, size, mode)
 {
        char sizebuf[32];
        FILE *file;
 {
        char sizebuf[32];
        FILE *file;
-       int retry = 0;
+       int retry = 0, tos;
 
        file_size = size;
        byte_count = 0;
 
        file_size = size;
        byte_count = 0;
@@ -672,6 +696,11 @@ dataconn(name, size, mode)
                }
                (void) close(pdata);
                pdata = s;
                }
                (void) close(pdata);
                pdata = s;
+#ifdef IP_TOS
+               tos = IPTOS_LOWDELAY;
+               (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
+                   sizeof(int));
+#endif
                reply(150, "Opening %s mode data connection for %s%s.",
                     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
                return(fdopen(pdata, mode));
                reply(150, "Opening %s mode data connection for %s%s.",
                     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
                return(fdopen(pdata, mode));
@@ -689,8 +718,7 @@ dataconn(name, size, mode)
        if (file == NULL) {
                reply(425, "Can't create data socket (%s,%d): %s.",
                    inet_ntoa(data_source.sin_addr),
        if (file == NULL) {
                reply(425, "Can't create data socket (%s,%d): %s.",
                    inet_ntoa(data_source.sin_addr),
-                   ntohs(data_source.sin_port),
-                   errno < sys_nerr ? sys_errlist[errno] : "unknown error");
+                   ntohs(data_source.sin_port), strerror(errno));
                return (NULL);
        }
        data = fileno(file);
                return (NULL);
        }
        data = fileno(file);
@@ -802,7 +830,7 @@ receive_data(instr, outstr)
        FILE *instr, *outstr;
 {
        register int c;
        FILE *instr, *outstr;
 {
        register int c;
-       int cnt;
+       int cnt, bare_lfs = 0;
        char buf[BUFSIZ];
 
        transflag++;
        char buf[BUFSIZ];
 
        transflag++;
@@ -832,6 +860,8 @@ receive_data(instr, outstr)
        case TYPE_A:
                while ((c = getc(instr)) != EOF) {
                        byte_count++;
        case TYPE_A:
                while ((c = getc(instr)) != EOF) {
                        byte_count++;
+                       if (c == '\n')
+                               bare_lfs++;
                        while (c == '\r') {
                                if (ferror(outstr))
                                        goto data_err;
                        while (c == '\r') {
                                if (ferror(outstr))
                                        goto data_err;
@@ -850,6 +880,10 @@ receive_data(instr, outstr)
                if (ferror(outstr))
                        goto file_err;
                transflag = 0;
                if (ferror(outstr))
                        goto file_err;
                transflag = 0;
+               if (bare_lfs) {
+                       lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
+                       printf("   File may not have transferred correctly.\r\n");
+               }
                return (0);
        default:
                reply(550, "Unimplemented TYPE %d in receive_data", type);
                return (0);
        default:
                reply(550, "Unimplemented TYPE %d in receive_data", type);
@@ -907,7 +941,7 @@ statcmd()
        lreply(211, "%s FTP server status:", hostname, version);
        printf("     %s\r\n", version);
        printf("     Connected to %s", remotehost);
        lreply(211, "%s FTP server status:", hostname, version);
        printf("     %s\r\n", version);
        printf("     Connected to %s", remotehost);
-       if (isdigit(remotehost[0]))
+       if (!isdigit(remotehost[0]))
                printf(" (%s)", inet_ntoa(his_addr.sin_addr));
        printf("\r\n");
        if (logged_in) {
                printf(" (%s)", inet_ntoa(his_addr.sin_addr));
        printf("\r\n");
        if (logged_in) {
@@ -1252,10 +1286,7 @@ perror_reply(code, string)
        int code;
        char *string;
 {
        int code;
        char *string;
 {
-       if (errno < sys_nerr)
-               reply(code, "%s: %s.", string, sys_errlist[errno]);
-       else
-               reply(code, "%s: unknown error %d.", string, errno);
+       reply(code, "%s: %s.", string, strerror(errno));
 }
 
 static char *onefile[] = {
 }
 
 static char *onefile[] = {
@@ -1271,6 +1302,7 @@ send_file_list(whichfiles)
        struct direct *dir;
        FILE *dout = NULL;
        register char **dirlist, *dirname;
        struct direct *dir;
        FILE *dout = NULL;
        register char **dirlist, *dirname;
+       int simple = 0;
        char *strpbrk();
 
        if (strpbrk(whichfiles, "~{[*?") != NULL) {
        char *strpbrk();
 
        if (strpbrk(whichfiles, "~{[*?") != NULL) {
@@ -1289,6 +1321,7 @@ send_file_list(whichfiles)
        } else {
                onefile[0] = whichfiles;
                dirlist = onefile;
        } else {
                onefile[0] = whichfiles;
                dirlist = onefile;
+               simple = 1;
        }
 
        if (setjmp(urgcatch)) {
        }
 
        if (setjmp(urgcatch)) {
@@ -1318,12 +1351,13 @@ send_file_list(whichfiles)
 
                if ((st.st_mode&S_IFMT) == S_IFREG) {
                        if (dout == NULL) {
 
                if ((st.st_mode&S_IFMT) == S_IFREG) {
                        if (dout == NULL) {
-                               dout = dataconn(whichfiles, (off_t)-1, "w");
+                               dout = dataconn("file list", (off_t)-1, "w");
                                if (dout == NULL)
                                        return;
                                transflag++;
                        }
                                if (dout == NULL)
                                        return;
                                transflag++;
                        }
-                       fprintf(dout, "%s\n", dirname);
+                       fprintf(dout, "%s%s\n", dirname,
+                               type == TYPE_A ? "\r" : "");
                        byte_count += strlen(dirname) + 1;
                        continue;
                } else if ((st.st_mode&S_IFMT) != S_IFDIR)
                        byte_count += strlen(dirname) + 1;
                        continue;
                } else if ((st.st_mode&S_IFMT) != S_IFDIR)
@@ -1347,19 +1381,21 @@ send_file_list(whichfiles)
                         * We have to do a stat to insure it's
                         * not a directory or special file.
                         */
                         * We have to do a stat to insure it's
                         * not a directory or special file.
                         */
-                       if (stat(nbuf, &st) == 0 &&
-                           (st.st_mode&S_IFMT) == S_IFREG) {
+                       if (simple || (stat(nbuf, &st) == 0 &&
+                           (st.st_mode&S_IFMT) == S_IFREG)) {
                                if (dout == NULL) {
                                if (dout == NULL) {
-                                       dout = dataconn(whichfiles, (off_t)-1,
+                                       dout = dataconn("file list", (off_t)-1,
                                                "w");
                                        if (dout == NULL)
                                                return;
                                        transflag++;
                                }
                                if (nbuf[0] == '.' && nbuf[1] == '/')
                                                "w");
                                        if (dout == NULL)
                                                return;
                                        transflag++;
                                }
                                if (nbuf[0] == '.' && nbuf[1] == '/')
-                                       fprintf(dout, "%s\n", &nbuf[2]);
+                                       fprintf(dout, "%s%s\n", &nbuf[2],
+                                               type == TYPE_A ? "\r" : "");
                                else
                                else
-                                       fprintf(dout, "%s\n", nbuf);
+                                       fprintf(dout, "%s%s\n", nbuf,
+                                               type == TYPE_A ? "\r" : "");
                                byte_count += strlen(nbuf) + 1;
                        }
                }
                                byte_count += strlen(nbuf) + 1;
                        }
                }