Index: ftpd.8 =================================================================== RCS file: /home/ncvs/src/libexec/ftpd/ftpd.8,v retrieving revision 1.29 diff -u -d -r1.29 ftpd.8 --- ftpd.8 1999/08/28 00:09:29 1.29 +++ ftpd.8 1999/10/12 16:32:22 @@ -43,14 +43,17 @@ .Nm ftpd .Op Fl d .Op Fl l Op Fl l +.Op Fl s .Op Fl A .Op Fl D +.Op Fl N .Op Fl R .Op Fl S .Op Fl U .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Op Fl a Ar address +.Op Fl n Ar path .Op Fl p Ar file .Sh DESCRIPTION .Nm Ftpd @@ -80,6 +83,10 @@ by default, and may have to be enabled in .Xr syslogd 8 Ns 's configuration file. +.It Fl s +Prevent guest users from deleting files from +.Xr sticky 8 +directories. .It Fl D With this option set, .Nm @@ -135,6 +142,13 @@ .Fl D is specified, write the daemon's process ID to .Ar file . +.It Fl n +If /var/run/nologin does not exist, also check for +.Ar path +and, if it exists, display it and exit. +.It Fl N +Do not allow anonymous ftp access, +even if the requirements described below are met. .It Fl A Allow only anonymous ftp access. .El @@ -440,6 +454,7 @@ .Xr getusershell 3 , .Xr login.conf 5 , .Xr inetd 8 , +.Xr sticky 8 , .Xr syslogd 8 .Sh BUGS The server must run as the super-user Index: ftpd.c =================================================================== RCS file: /home/ncvs/src/libexec/ftpd/ftpd.c,v retrieving revision 1.59 diff -u -d -r1.59 ftpd.c --- ftpd.c 1999/09/19 22:05:29 1.59 +++ ftpd.c 1999/10/14 13:56:49 @@ -132,6 +132,8 @@ int restricted_data_ports = 1; int paranoid = 1; /* be extra careful about security */ int anon_only = 0; /* Only anonymous ftp allowed */ +int no_anon = 0; /* Anonymous ftp not allowed */ +int sticky_guests = 0;/* Guests may not delete files in sticky dirs */ int guest; int dochroot; int stats; @@ -154,6 +156,8 @@ char *hostname; #ifdef VIRTUAL_HOSTING char *ftpuser; +int use_ftp_nologin = 0; +char *ftp_nologin_path; static struct ftphost { struct ftphost *next; @@ -281,7 +285,7 @@ bind_address.s_addr = htonl(INADDR_ANY); - while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { + while ((ch = getopt(argc, argv, "ANdlsDSURt:T:u:va:n:p:")) != -1) { switch (ch) { case 'D': daemon_mode++; @@ -295,6 +299,10 @@ logging++; /* > 1 == extra logging */ break; + case 's': + sticky_guests++; + break; + case 'R': paranoid = 0; break; @@ -340,9 +348,20 @@ break; } case 'A': + no_anon = 0; anon_only = 1; break; + case 'n': + ftp_nologin_path = optarg; + use_ftp_nologin = 1; + break; + + case 'N': + anon_only = 0; + no_anon = 1; + break; + case 'v': debug = 1; break; @@ -509,7 +528,8 @@ tmpline[0] = '\0'; /* If logins are disabled, print out the message. */ - if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { + if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL || + (use_ftp_nologin && (fd = fopen(ftp_nologin_path,"r")) != NULL)) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; @@ -748,7 +768,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. @@ -780,7 +800,9 @@ guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { - if (checkuser(_PATH_FTPUSERS, "ftp", 0) || + if (no_anon) + reply(530, "User %s access denied.", name); + else if (checkuser(_PATH_FTPUSERS, "ftp", 0) || checkuser(_PATH_FTPUSERS, "anonymous", 0)) reply(530, "User %s access denied.", name); #ifdef VIRTUAL_HOSTING @@ -1914,8 +1936,46 @@ char *name; { struct stat st; + char *dir, *p; + int chopped; LOGCMD("delete", name); + if (sticky_guests && guest) { + chopped = 0; + p = strrchr(name, '/'); + if (p == NULL) { + dir = "."; + } else if (p == name) { + dir = "/"; + } else if (p == name + strlen(name) - 1) { + dir = NULL; + } else { + /* + * Replace the last '/' with a nul-terminator, to + * be restored after the call to stat(), to avoid + * a malloc() per deletion. + */ + chopped = 1; + dir = name; + p[0] = '\0'; + } + if (dir != NULL) { + if (stat(dir, &st) < 0) { + perror_reply(550, dir); + if (chopped) + p[0] = '/'; + return; + } else if (st.st_mode&S_ISVTX) { + errno = EPERM; + if (chopped) + p[0] = '/'; + perror_reply(550, name); + return; + } else if (chopped) { + p[0] = '/'; + } + } + } if (stat(name, &st) < 0) { perror_reply(550, name); return;