155682Smarkm/* 255682Smarkm * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 355682Smarkm * The Regents of the University of California. All rights reserved. 455682Smarkm * 555682Smarkm * Redistribution and use in source and binary forms, with or without 655682Smarkm * modification, are permitted provided that the following conditions 755682Smarkm * are met: 855682Smarkm * 1. Redistributions of source code must retain the above copyright 955682Smarkm * notice, this list of conditions and the following disclaimer. 1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer in the 1255682Smarkm * documentation and/or other materials provided with the distribution. 1355682Smarkm * 3. All advertising materials mentioning features or use of this software 1455682Smarkm * must display the following acknowledgement: 1555682Smarkm * This product includes software developed by the University of 1655682Smarkm * California, Berkeley and its contributors. 1755682Smarkm * 4. Neither the name of the University nor the names of its contributors 1855682Smarkm * may be used to endorse or promote products derived from this software 1955682Smarkm * without specific prior written permission. 2055682Smarkm * 2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155682Smarkm * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#define FTP_NAMES 3555682Smarkm#include "ftpd_locl.h" 3655682Smarkm#ifdef KRB5 3755682Smarkm#include <krb5.h> 3855682Smarkm#endif 3955682Smarkm#include "getarg.h" 4055682Smarkm 41233294SstasRCSID("$Id$"); 4255682Smarkm 4355682Smarkmstatic char version[] = "Version 6.00"; 4455682Smarkm 4555682Smarkmextern off_t restart_point; 4655682Smarkmextern char cbuf[]; 4755682Smarkm 4855682Smarkmstruct sockaddr_storage ctrl_addr_ss; 4955682Smarkmstruct sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss; 5055682Smarkm 5155682Smarkmstruct sockaddr_storage data_source_ss; 5255682Smarkmstruct sockaddr *data_source = (struct sockaddr *)&data_source_ss; 5355682Smarkm 5455682Smarkmstruct sockaddr_storage data_dest_ss; 5555682Smarkmstruct sockaddr *data_dest = (struct sockaddr *)&data_dest_ss; 5655682Smarkm 5755682Smarkmstruct sockaddr_storage his_addr_ss; 5855682Smarkmstruct sockaddr *his_addr = (struct sockaddr *)&his_addr_ss; 5955682Smarkm 6055682Smarkmstruct sockaddr_storage pasv_addr_ss; 6155682Smarkmstruct sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss; 6255682Smarkm 6355682Smarkmint data; 6455682Smarkmint logged_in; 6555682Smarkmstruct passwd *pw; 6655682Smarkmint debug = 0; 6755682Smarkmint ftpd_timeout = 900; /* timeout after 15 minutes of inactivity */ 6855682Smarkmint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 6990926Snectarint restricted_data_ports = 1; 7055682Smarkmint logging; 7155682Smarkmint guest; 7255682Smarkmint dochroot; 7355682Smarkmint type; 7455682Smarkmint form; 7555682Smarkmint stru; /* avoid C keyword */ 7655682Smarkmint mode; 7755682Smarkmint usedefault = 1; /* for data transfers */ 7855682Smarkmint pdata = -1; /* for passive mode */ 79142403Snectarint allow_insecure_oob = 1; 80142403Snectarstatic int transflag; 81142403Snectarstatic int urgflag; 8255682Smarkmoff_t file_size; 8355682Smarkmoff_t byte_count; 8455682Smarkm#if !defined(CMASK) || CMASK == 0 8555682Smarkm#undef CMASK 8655682Smarkm#define CMASK 027 8755682Smarkm#endif 8855682Smarkmint defumask = CMASK; /* default umask value */ 8955682Smarkmint guest_umask = 0777; /* Paranoia for anonymous users */ 9055682Smarkmchar tmpline[10240]; 9155682Smarkmchar hostname[MaxHostNameLen]; 9255682Smarkmchar remotehost[MaxHostNameLen]; 9355682Smarkmstatic char ttyline[20]; 94233294Sstasint paranoid = 1; 9555682Smarkm 9655682Smarkm#define AUTH_PLAIN (1 << 0) /* allow sending passwords */ 9755682Smarkm#define AUTH_OTP (1 << 1) /* passwords are one-time */ 9855682Smarkm#define AUTH_FTP (1 << 2) /* allow anonymous login */ 9955682Smarkm 10055682Smarkmstatic int auth_level = 0; /* Only allow kerberos login by default */ 10155682Smarkm 10255682Smarkm/* 10355682Smarkm * Timeout intervals for retrying connections 10455682Smarkm * to hosts that don't accept PORT cmds. This 10555682Smarkm * is a kludge, but given the problems with TCP... 10655682Smarkm */ 10755682Smarkm#define SWAITMAX 90 /* wait at most 90 seconds */ 10855682Smarkm#define SWAITINT 5 /* interval between retries */ 10955682Smarkm 11055682Smarkmint swaitmax = SWAITMAX; 11155682Smarkmint swaitint = SWAITINT; 11255682Smarkm 11355682Smarkm#ifdef HAVE_SETPROCTITLE 11455682Smarkmchar proctitle[BUFSIZ]; /* initial part of title */ 11555682Smarkm#endif /* HAVE_SETPROCTITLE */ 11655682Smarkm 11755682Smarkm#define LOGCMD(cmd, file) \ 11855682Smarkm if (logging > 1) \ 11955682Smarkm syslog(LOG_INFO,"%s %s%s", cmd, \ 12055682Smarkm *(file) == '/' ? "" : curdir(), file); 12155682Smarkm#define LOGCMD2(cmd, file1, file2) \ 12255682Smarkm if (logging > 1) \ 12355682Smarkm syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 12455682Smarkm *(file1) == '/' ? "" : curdir(), file1, \ 12555682Smarkm *(file2) == '/' ? "" : curdir(), file2); 12655682Smarkm#define LOGBYTES(cmd, file, cnt) \ 12755682Smarkm if (logging > 1) { \ 12855682Smarkm if (cnt == (off_t)-1) \ 12955682Smarkm syslog(LOG_INFO,"%s %s%s", cmd, \ 13055682Smarkm *(file) == '/' ? "" : curdir(), file); \ 13155682Smarkm else \ 13255682Smarkm syslog(LOG_INFO, "%s %s%s = %ld bytes", \ 13355682Smarkm cmd, (*(file) == '/') ? "" : curdir(), file, (long)cnt); \ 13455682Smarkm } 13555682Smarkm 13655682Smarkmstatic void ack (char *); 13755682Smarkmstatic void myoob (int); 138142403Snectarstatic int handleoobcmd(void); 13955682Smarkmstatic int checkuser (char *, char *); 14055682Smarkmstatic int checkaccess (char *); 14155682Smarkmstatic FILE *dataconn (const char *, off_t, const char *); 142178825Sdfrstatic void dolog (struct sockaddr *, int); 14355682Smarkmstatic void end_login (void); 144178825Sdfrstatic FILE *getdatasock (const char *, int); 14555682Smarkmstatic char *gunique (char *); 14655682Smarkmstatic RETSIGTYPE lostconn (int); 14755682Smarkmstatic int receive_data (FILE *, FILE *); 14855682Smarkmstatic void send_data (FILE *, FILE *); 14955682Smarkmstatic struct passwd * sgetpwnam (char *); 15055682Smarkm 15155682Smarkmstatic char * 15255682Smarkmcurdir(void) 15355682Smarkm{ 15455682Smarkm static char path[MaxPathLen+1]; /* path + '/' + '\0' */ 15555682Smarkm 15655682Smarkm if (getcwd(path, sizeof(path)-1) == NULL) 15755682Smarkm return (""); 15855682Smarkm if (path[1] != '\0') /* special case for root dir. */ 15955682Smarkm strlcat(path, "/", sizeof(path)); 16055682Smarkm /* For guest account, skip / since it's chrooted */ 16155682Smarkm return (guest ? path+1 : path); 16255682Smarkm} 16355682Smarkm 16455682Smarkm#ifndef LINE_MAX 16555682Smarkm#define LINE_MAX 1024 16655682Smarkm#endif 16755682Smarkm 16855682Smarkmstatic int 16955682Smarkmparse_auth_level(char *str) 17055682Smarkm{ 17155682Smarkm char *p; 17255682Smarkm int ret = 0; 17355682Smarkm char *foo = NULL; 17455682Smarkm 17555682Smarkm for(p = strtok_r(str, ",", &foo); 17655682Smarkm p; 17755682Smarkm p = strtok_r(NULL, ",", &foo)) { 17855682Smarkm if(strcmp(p, "user") == 0) 17955682Smarkm ; 18055682Smarkm#ifdef OTP 18155682Smarkm else if(strcmp(p, "otp") == 0) 18255682Smarkm ret |= AUTH_PLAIN|AUTH_OTP; 18355682Smarkm#endif 18455682Smarkm else if(strcmp(p, "ftp") == 0 || 18555682Smarkm strcmp(p, "safe") == 0) 18655682Smarkm ret |= AUTH_FTP; 18755682Smarkm else if(strcmp(p, "plain") == 0) 18855682Smarkm ret |= AUTH_PLAIN; 18955682Smarkm else if(strcmp(p, "none") == 0) 19055682Smarkm ret |= AUTH_PLAIN|AUTH_FTP; 19155682Smarkm else 19255682Smarkm warnx("bad value for -a: `%s'", p); 19355682Smarkm } 194233294Sstas return ret; 19555682Smarkm} 19655682Smarkm 19755682Smarkm/* 19855682Smarkm * Print usage and die. 19955682Smarkm */ 20055682Smarkm 20155682Smarkmstatic int interactive_flag; 20255682Smarkmstatic char *guest_umask_string; 20355682Smarkmstatic char *port_string; 20455682Smarkmstatic char *umask_string; 20555682Smarkmstatic char *auth_string; 20655682Smarkm 20755682Smarkmint use_builtin_ls = -1; 20855682Smarkm 20955682Smarkmstatic int help_flag; 21055682Smarkmstatic int version_flag; 21155682Smarkm 21272445Sassarstatic const char *good_chars = "+-=_,."; 21372445Sassar 21455682Smarkmstruct getargs args[] = { 21555682Smarkm { NULL, 'a', arg_string, &auth_string, "required authentication" }, 21655682Smarkm { NULL, 'i', arg_flag, &interactive_flag, "don't assume stdin is a socket" }, 21755682Smarkm { NULL, 'p', arg_string, &port_string, "what port to listen to" }, 21855682Smarkm { NULL, 'g', arg_string, &guest_umask_string, "umask for guest logins" }, 21955682Smarkm { NULL, 'l', arg_counter, &logging, "log more stuff", "" }, 22055682Smarkm { NULL, 't', arg_integer, &ftpd_timeout, "initial timeout" }, 22155682Smarkm { NULL, 'T', arg_integer, &maxtimeout, "max timeout" }, 22255682Smarkm { NULL, 'u', arg_string, &umask_string, "umask for user logins" }, 22390926Snectar { NULL, 'U', arg_negative_flag, &restricted_data_ports, "don't use high data ports" }, 22472445Sassar { NULL, 'd', arg_flag, &debug, "enable debugging" }, 22572445Sassar { NULL, 'v', arg_flag, &debug, "enable debugging" }, 22655682Smarkm { "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" }, 22772445Sassar { "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" }, 228142403Snectar { "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" }, 229233294Sstas#ifdef KRB5 230127808Snectar { "gss-bindings", 0, arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL}, 231127808Snectar#endif 23255682Smarkm { "version", 0, arg_flag, &version_flag }, 23355682Smarkm { "help", 'h', arg_flag, &help_flag } 23455682Smarkm}; 23555682Smarkm 23655682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]); 23755682Smarkm 23855682Smarkmstatic void 23955682Smarkmusage (int code) 24055682Smarkm{ 24155682Smarkm arg_printusage(args, num_args, NULL, ""); 24255682Smarkm exit (code); 24355682Smarkm} 24455682Smarkm 24555682Smarkm/* output contents of a file */ 24655682Smarkmstatic int 24755682Smarkmshow_file(const char *file, int code) 24855682Smarkm{ 24955682Smarkm FILE *f; 25055682Smarkm char buf[128]; 25155682Smarkm 25255682Smarkm f = fopen(file, "r"); 25355682Smarkm if(f == NULL) 25455682Smarkm return -1; 25555682Smarkm while(fgets(buf, sizeof(buf), f)){ 25655682Smarkm buf[strcspn(buf, "\r\n")] = '\0'; 25755682Smarkm lreply(code, "%s", buf); 25855682Smarkm } 25955682Smarkm fclose(f); 26055682Smarkm return 0; 26155682Smarkm} 26255682Smarkm 26355682Smarkmint 26455682Smarkmmain(int argc, char **argv) 26555682Smarkm{ 26672445Sassar socklen_t his_addr_len, ctrl_addr_len; 26772445Sassar int on = 1; 26855682Smarkm int port; 26955682Smarkm struct servent *sp; 27055682Smarkm 27155682Smarkm int optind = 0; 27255682Smarkm 27378527Sassar setprogname (argv[0]); 27455682Smarkm 27555682Smarkm if(getarg(args, num_args, argc, argv, &optind)) 27655682Smarkm usage(1); 27755682Smarkm 27855682Smarkm if(help_flag) 27955682Smarkm usage(0); 280233294Sstas 28155682Smarkm if(version_flag) { 28255682Smarkm print_version(NULL); 28355682Smarkm exit(0); 28455682Smarkm } 28555682Smarkm 28655682Smarkm if(auth_string) 28755682Smarkm auth_level = parse_auth_level(auth_string); 28855682Smarkm { 28955682Smarkm char *p; 29055682Smarkm long val = 0; 291233294Sstas 29255682Smarkm if(guest_umask_string) { 29355682Smarkm val = strtol(guest_umask_string, &p, 8); 29455682Smarkm if (*p != '\0' || val < 0) 29555682Smarkm warnx("bad value for -g"); 29655682Smarkm else 29755682Smarkm guest_umask = val; 29855682Smarkm } 29955682Smarkm if(umask_string) { 30055682Smarkm val = strtol(umask_string, &p, 8); 30155682Smarkm if (*p != '\0' || val < 0) 30255682Smarkm warnx("bad value for -u"); 30355682Smarkm else 30455682Smarkm defumask = val; 30555682Smarkm } 30655682Smarkm } 307102644Snectar sp = getservbyname("ftp", "tcp"); 308102644Snectar if(sp) 309102644Snectar port = sp->s_port; 310102644Snectar else 311102644Snectar port = htons(21); 31255682Smarkm if(port_string) { 31355682Smarkm sp = getservbyname(port_string, "tcp"); 31455682Smarkm if(sp) 31555682Smarkm port = sp->s_port; 31655682Smarkm else 317120945Snectar if(isdigit((unsigned char)port_string[0])) 31855682Smarkm port = htons(atoi(port_string)); 31955682Smarkm else 32055682Smarkm warnx("bad value for -p"); 32155682Smarkm } 322233294Sstas 32355682Smarkm if (maxtimeout < ftpd_timeout) 32455682Smarkm maxtimeout = ftpd_timeout; 32555682Smarkm 32655682Smarkm#if 0 32755682Smarkm if (ftpd_timeout > maxtimeout) 32855682Smarkm ftpd_timeout = maxtimeout; 32955682Smarkm#endif 33055682Smarkm 33155682Smarkm if(interactive_flag) 332233294Sstas mini_inetd(port, NULL); 33355682Smarkm 33455682Smarkm /* 33555682Smarkm * LOG_NDELAY sets up the logging connection immediately, 33655682Smarkm * necessary for anonymous ftp's that chroot and can't do it later. 33755682Smarkm */ 33855682Smarkm openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 33955682Smarkm his_addr_len = sizeof(his_addr_ss); 34055682Smarkm if (getpeername(STDIN_FILENO, his_addr, &his_addr_len) < 0) { 34155682Smarkm syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 34255682Smarkm exit(1); 34355682Smarkm } 34455682Smarkm ctrl_addr_len = sizeof(ctrl_addr_ss); 34555682Smarkm if (getsockname(STDIN_FILENO, ctrl_addr, &ctrl_addr_len) < 0) { 34655682Smarkm syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 34755682Smarkm exit(1); 34855682Smarkm } 349233294Sstas#if defined(IP_TOS) 350233294Sstas if (ctrl_addr->sa_family == AF_INET) 351233294Sstas socket_set_tos(STDIN_FILENO, IP_TOS); 35255682Smarkm#endif 35355682Smarkm data_source->sa_family = ctrl_addr->sa_family; 35455682Smarkm socket_set_port (data_source, 35555682Smarkm htons(ntohs(socket_get_port(ctrl_addr)) - 1)); 35655682Smarkm 35755682Smarkm /* set this here so it can be put in wtmp */ 35855682Smarkm snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid()); 35955682Smarkm 36055682Smarkm 36155682Smarkm /* freopen(_PATH_DEVNULL, "w", stderr); */ 36255682Smarkm signal(SIGPIPE, lostconn); 36355682Smarkm signal(SIGCHLD, SIG_IGN); 36455682Smarkm#ifdef SIGURG 36555682Smarkm if (signal(SIGURG, myoob) == SIG_ERR) 36655682Smarkm syslog(LOG_ERR, "signal: %m"); 36755682Smarkm#endif 36855682Smarkm 36955682Smarkm /* Try to handle urgent data inline */ 37055682Smarkm#if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT) 37155682Smarkm if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (void *)&on, 37255682Smarkm sizeof(on)) < 0) 37355682Smarkm syslog(LOG_ERR, "setsockopt: %m"); 37455682Smarkm#endif 37555682Smarkm 37655682Smarkm#ifdef F_SETOWN 37755682Smarkm if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 37855682Smarkm syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 37955682Smarkm#endif 38055682Smarkm dolog(his_addr, his_addr_len); 38155682Smarkm /* 38255682Smarkm * Set up default state 38355682Smarkm */ 38455682Smarkm data = -1; 38555682Smarkm type = TYPE_A; 38655682Smarkm form = FORM_N; 38755682Smarkm stru = STRU_F; 38855682Smarkm mode = MODE_S; 38955682Smarkm tmpline[0] = '\0'; 39055682Smarkm 39155682Smarkm /* If logins are disabled, print out the message. */ 39255682Smarkm if(show_file(_PATH_NOLOGIN, 530) == 0) { 39355682Smarkm reply(530, "System not available."); 39455682Smarkm exit(0); 39555682Smarkm } 39655682Smarkm show_file(_PATH_FTPWELCOME, 220); 39755682Smarkm /* reply(220,) must follow */ 39855682Smarkm gethostname(hostname, sizeof(hostname)); 399233294Sstas 40055682Smarkm reply(220, "%s FTP server (%s" 40155682Smarkm#ifdef KRB5 40255682Smarkm "+%s" 40355682Smarkm#endif 40455682Smarkm ") ready.", hostname, version 40555682Smarkm#ifdef KRB5 40655682Smarkm ,heimdal_version 40755682Smarkm#endif 40855682Smarkm ); 40955682Smarkm 41055682Smarkm for (;;) 41155682Smarkm yyparse(); 41255682Smarkm /* NOTREACHED */ 41355682Smarkm} 41455682Smarkm 41555682Smarkmstatic RETSIGTYPE 41655682Smarkmlostconn(int signo) 41755682Smarkm{ 41855682Smarkm 41955682Smarkm if (debug) 42055682Smarkm syslog(LOG_DEBUG, "lost connection"); 42155682Smarkm dologout(-1); 42255682Smarkm} 42355682Smarkm 42455682Smarkm/* 42555682Smarkm * Helper function for sgetpwnam(). 42655682Smarkm */ 42755682Smarkmstatic char * 42855682Smarkmsgetsave(char *s) 42955682Smarkm{ 43055682Smarkm char *new = strdup(s); 43155682Smarkm 43255682Smarkm if (new == NULL) { 43355682Smarkm perror_reply(421, "Local resource failure: malloc"); 43455682Smarkm dologout(1); 43555682Smarkm /* NOTREACHED */ 43655682Smarkm } 43755682Smarkm return new; 43855682Smarkm} 43955682Smarkm 44055682Smarkm/* 44155682Smarkm * Save the result of a getpwnam. Used for USER command, since 44255682Smarkm * the data returned must not be clobbered by any other command 44355682Smarkm * (e.g., globbing). 44455682Smarkm */ 44555682Smarkmstatic struct passwd * 44655682Smarkmsgetpwnam(char *name) 44755682Smarkm{ 44855682Smarkm static struct passwd save; 44955682Smarkm struct passwd *p; 45055682Smarkm 45155682Smarkm if ((p = k_getpwnam(name)) == NULL) 45255682Smarkm return (p); 45355682Smarkm if (save.pw_name) { 45455682Smarkm free(save.pw_name); 45555682Smarkm free(save.pw_passwd); 45655682Smarkm free(save.pw_gecos); 45755682Smarkm free(save.pw_dir); 45855682Smarkm free(save.pw_shell); 45955682Smarkm } 46055682Smarkm save = *p; 46155682Smarkm save.pw_name = sgetsave(p->pw_name); 46255682Smarkm save.pw_passwd = sgetsave(p->pw_passwd); 46355682Smarkm save.pw_gecos = sgetsave(p->pw_gecos); 46455682Smarkm save.pw_dir = sgetsave(p->pw_dir); 46555682Smarkm save.pw_shell = sgetsave(p->pw_shell); 46655682Smarkm return (&save); 46755682Smarkm} 46855682Smarkm 46955682Smarkmstatic int login_attempts; /* number of failed login attempts */ 47055682Smarkmstatic int askpasswd; /* had user command, ask for passwd */ 47155682Smarkmstatic char curname[10]; /* current USER name */ 47255682Smarkm#ifdef OTP 47355682SmarkmOtpContext otp_ctx; 47455682Smarkm#endif 47555682Smarkm 47655682Smarkm/* 47755682Smarkm * USER command. 47855682Smarkm * Sets global passwd pointer pw if named account exists and is acceptable; 47955682Smarkm * sets askpasswd if a PASS command is expected. If logged in previously, 48055682Smarkm * need to reset state. If name is "ftp" or "anonymous", the name is not in 48155682Smarkm * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 48255682Smarkm * If account doesn't exist, ask for passwd anyway. Otherwise, check user 48355682Smarkm * requesting login privileges. Disallow anyone who does not have a standard 48455682Smarkm * shell as returned by getusershell(). Disallow anyone mentioned in the file 48555682Smarkm * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 48655682Smarkm */ 48755682Smarkmvoid 48855682Smarkmuser(char *name) 48955682Smarkm{ 49055682Smarkm char *cp, *shell; 49155682Smarkm 49255682Smarkm if(auth_level == 0 && !sec_complete){ 49355682Smarkm reply(530, "No login allowed without authorization."); 49455682Smarkm return; 49555682Smarkm } 49655682Smarkm 49755682Smarkm if (logged_in) { 49855682Smarkm if (guest) { 49955682Smarkm reply(530, "Can't change user from guest login."); 50055682Smarkm return; 50155682Smarkm } else if (dochroot) { 50255682Smarkm reply(530, "Can't change user from chroot user."); 50355682Smarkm return; 50455682Smarkm } 50555682Smarkm end_login(); 50655682Smarkm } 50755682Smarkm 50855682Smarkm guest = 0; 50955682Smarkm if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 51055682Smarkm if ((auth_level & AUTH_FTP) == 0 || 511233294Sstas checkaccess("ftp") || 51255682Smarkm checkaccess("anonymous")) 51355682Smarkm reply(530, "User %s access denied.", name); 51455682Smarkm else if ((pw = sgetpwnam("ftp")) != NULL) { 51555682Smarkm guest = 1; 51655682Smarkm defumask = guest_umask; /* paranoia for incoming */ 51755682Smarkm askpasswd = 1; 51855682Smarkm reply(331, "Guest login ok, type your name as password."); 51955682Smarkm } else 52055682Smarkm reply(530, "User %s unknown.", name); 52155682Smarkm if (!askpasswd && logging) { 52255682Smarkm char data_addr[256]; 52355682Smarkm 52455682Smarkm if (inet_ntop (his_addr->sa_family, 52555682Smarkm socket_get_address(his_addr), 52655682Smarkm data_addr, sizeof(data_addr)) == NULL) 52755682Smarkm strlcpy (data_addr, "unknown address", 52855682Smarkm sizeof(data_addr)); 52955682Smarkm 53055682Smarkm syslog(LOG_NOTICE, 53155682Smarkm "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)", 53255682Smarkm remotehost, data_addr); 53355682Smarkm } 53455682Smarkm return; 53555682Smarkm } 53655682Smarkm if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){ 53755682Smarkm reply(530, "Only authorized and anonymous login allowed."); 53855682Smarkm return; 53955682Smarkm } 54055682Smarkm if ((pw = sgetpwnam(name))) { 54155682Smarkm if ((shell = pw->pw_shell) == NULL || *shell == 0) 54255682Smarkm shell = _PATH_BSHELL; 54355682Smarkm while ((cp = getusershell()) != NULL) 54455682Smarkm if (strcmp(cp, shell) == 0) 54555682Smarkm break; 54655682Smarkm endusershell(); 54755682Smarkm 54855682Smarkm if (cp == NULL || checkaccess(name)) { 54955682Smarkm reply(530, "User %s access denied.", name); 55055682Smarkm if (logging) { 55155682Smarkm char data_addr[256]; 55255682Smarkm 55355682Smarkm if (inet_ntop (his_addr->sa_family, 55455682Smarkm socket_get_address(his_addr), 55555682Smarkm data_addr, 55655682Smarkm sizeof(data_addr)) == NULL) 55755682Smarkm strlcpy (data_addr, 55855682Smarkm "unknown address", 55955682Smarkm sizeof(data_addr)); 56055682Smarkm 56155682Smarkm syslog(LOG_NOTICE, 56255682Smarkm "FTP LOGIN REFUSED FROM %s(%s), %s", 56355682Smarkm remotehost, 56455682Smarkm data_addr, 56555682Smarkm name); 56655682Smarkm } 56755682Smarkm pw = (struct passwd *) NULL; 56855682Smarkm return; 56955682Smarkm } 57055682Smarkm } 57155682Smarkm if (logging) 57255682Smarkm strlcpy(curname, name, sizeof(curname)); 57355682Smarkm if(sec_complete) { 574178825Sdfr if(sec_userok(name) == 0) { 57555682Smarkm do_login(232, name); 576178825Sdfr sec_session(name); 577178825Sdfr } else 57855682Smarkm reply(530, "User %s access denied.", name); 57955682Smarkm } else { 580178825Sdfr#ifdef OTP 58155682Smarkm char ss[256]; 58255682Smarkm 58355682Smarkm if (otp_challenge(&otp_ctx, name, ss, sizeof(ss)) == 0) { 58455682Smarkm reply(331, "Password %s for %s required.", 58555682Smarkm ss, name); 58655682Smarkm askpasswd = 1; 58755682Smarkm } else 58855682Smarkm#endif 58955682Smarkm if ((auth_level & AUTH_OTP) == 0) { 59055682Smarkm reply(331, "Password required for %s.", name); 59155682Smarkm askpasswd = 1; 59255682Smarkm } else { 593178825Sdfr#ifdef OTP 59455682Smarkm char *s; 595178825Sdfr 59655682Smarkm if ((s = otp_error (&otp_ctx)) != NULL) 59755682Smarkm lreply(530, "OTP: %s", s); 59855682Smarkm#endif 59955682Smarkm reply(530, 60055682Smarkm "Only authorized, anonymous" 60155682Smarkm#ifdef OTP 60255682Smarkm " and OTP " 60355682Smarkm#endif 60455682Smarkm "login allowed."); 60555682Smarkm } 60655682Smarkm 60755682Smarkm } 60855682Smarkm /* 60955682Smarkm * Delay before reading passwd after first failed 61055682Smarkm * attempt to slow down passwd-guessing programs. 61155682Smarkm */ 61255682Smarkm if (login_attempts) 61355682Smarkm sleep(login_attempts); 61455682Smarkm} 61555682Smarkm 61655682Smarkm/* 61755682Smarkm * Check if a user is in the file "fname" 61855682Smarkm */ 61955682Smarkmstatic int 62055682Smarkmcheckuser(char *fname, char *name) 62155682Smarkm{ 62255682Smarkm FILE *fd; 62355682Smarkm int found = 0; 62455682Smarkm char *p, line[BUFSIZ]; 62555682Smarkm 62655682Smarkm if ((fd = fopen(fname, "r")) != NULL) { 62755682Smarkm while (fgets(line, sizeof(line), fd) != NULL) 62855682Smarkm if ((p = strchr(line, '\n')) != NULL) { 62955682Smarkm *p = '\0'; 63055682Smarkm if (line[0] == '#') 63155682Smarkm continue; 63255682Smarkm if (strcmp(line, name) == 0) { 63355682Smarkm found = 1; 63455682Smarkm break; 63555682Smarkm } 63655682Smarkm } 63755682Smarkm fclose(fd); 63855682Smarkm } 63955682Smarkm return (found); 64055682Smarkm} 64155682Smarkm 64255682Smarkm 64355682Smarkm/* 644233294Sstas * Determine whether a user has access, based on information in 64555682Smarkm * _PATH_FTPUSERS. The users are listed one per line, with `allow' 64655682Smarkm * or `deny' after the username. If anything other than `allow', or 64755682Smarkm * just nothing, is given after the username, `deny' is assumed. 64855682Smarkm * 64955682Smarkm * If the user is not found in the file, but the pseudo-user `*' is, 65055682Smarkm * the permission is taken from that line. 65155682Smarkm * 65255682Smarkm * This preserves the old semantics where if a user was listed in the 65355682Smarkm * file he was denied, otherwise he was allowed. 65455682Smarkm * 65555682Smarkm * Return 1 if the user is denied, or 0 if he is allowed. */ 65655682Smarkm 65755682Smarkmstatic int 65855682Smarkmmatch(const char *pattern, const char *string) 65955682Smarkm{ 66055682Smarkm return fnmatch(pattern, string, FNM_NOESCAPE); 66155682Smarkm} 66255682Smarkm 66355682Smarkmstatic int 66455682Smarkmcheckaccess(char *name) 66555682Smarkm{ 66655682Smarkm#define ALLOWED 0 66755682Smarkm#define NOT_ALLOWED 1 66855682Smarkm FILE *fd; 66955682Smarkm int allowed = ALLOWED; 67055682Smarkm char *user, *perm, line[BUFSIZ]; 67155682Smarkm char *foo; 672233294Sstas 67355682Smarkm fd = fopen(_PATH_FTPUSERS, "r"); 674233294Sstas 67555682Smarkm if(fd == NULL) 67655682Smarkm return allowed; 67755682Smarkm 67855682Smarkm while (fgets(line, sizeof(line), fd) != NULL) { 67955682Smarkm foo = NULL; 68055682Smarkm user = strtok_r(line, " \t\n", &foo); 68155682Smarkm if (user == NULL || user[0] == '#') 68255682Smarkm continue; 68355682Smarkm perm = strtok_r(NULL, " \t\n", &foo); 68455682Smarkm if (match(user, name) == 0){ 68555682Smarkm if(perm && strcmp(perm, "allow") == 0) 68655682Smarkm allowed = ALLOWED; 68755682Smarkm else 68855682Smarkm allowed = NOT_ALLOWED; 68955682Smarkm break; 69055682Smarkm } 69155682Smarkm } 69255682Smarkm fclose(fd); 69355682Smarkm return allowed; 69455682Smarkm} 69555682Smarkm#undef ALLOWED 69655682Smarkm#undef NOT_ALLOWED 69755682Smarkm 69855682Smarkm 69955682Smarkmint do_login(int code, char *passwd) 70055682Smarkm{ 70155682Smarkm login_attempts = 0; /* this time successful */ 70255682Smarkm if (setegid((gid_t)pw->pw_gid) < 0) { 70355682Smarkm reply(550, "Can't set gid."); 70455682Smarkm return -1; 70555682Smarkm } 70655682Smarkm initgroups(pw->pw_name, pw->pw_gid); 707233294Sstas#if defined(KRB5) 708178825Sdfr if(k_hasafs()) 709178825Sdfr k_setpag(); 710178825Sdfr#endif 71155682Smarkm 71255682Smarkm /* open wtmp before chroot */ 71355682Smarkm ftpd_logwtmp(ttyline, pw->pw_name, remotehost); 71455682Smarkm logged_in = 1; 71555682Smarkm 71655682Smarkm dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); 71755682Smarkm if (guest) { 71855682Smarkm /* 71955682Smarkm * We MUST do a chdir() after the chroot. Otherwise 72055682Smarkm * the old current directory will be accessible as "." 72155682Smarkm * outside the new root! 72255682Smarkm */ 72355682Smarkm if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 72455682Smarkm reply(550, "Can't set guest privileges."); 72555682Smarkm return -1; 72655682Smarkm } 72755682Smarkm } else if (dochroot) { 72855682Smarkm if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 72955682Smarkm reply(550, "Can't change root."); 73055682Smarkm return -1; 73155682Smarkm } 73255682Smarkm } else if (chdir(pw->pw_dir) < 0) { 73355682Smarkm if (chdir("/") < 0) { 73455682Smarkm reply(530, "User %s: can't change directory to %s.", 73555682Smarkm pw->pw_name, pw->pw_dir); 73655682Smarkm return -1; 73755682Smarkm } else 73855682Smarkm lreply(code, "No directory! Logging in with home=/"); 73955682Smarkm } 74055682Smarkm if (seteuid((uid_t)pw->pw_uid) < 0) { 74155682Smarkm reply(550, "Can't set uid."); 74255682Smarkm return -1; 74355682Smarkm } 74455682Smarkm 74555682Smarkm if(use_builtin_ls == -1) { 74655682Smarkm struct stat st; 74755682Smarkm /* if /bin/ls exist and is a regular file, use it, otherwise 74855682Smarkm use built-in ls */ 74955682Smarkm if(stat("/bin/ls", &st) == 0 && 75055682Smarkm S_ISREG(st.st_mode)) 75155682Smarkm use_builtin_ls = 0; 75255682Smarkm else 75355682Smarkm use_builtin_ls = 1; 75455682Smarkm } 75555682Smarkm 75655682Smarkm /* 75755682Smarkm * Display a login message, if it exists. 75855682Smarkm * N.B. reply(code,) must follow the message. 75955682Smarkm */ 76055682Smarkm show_file(_PATH_FTPLOGINMESG, code); 76155682Smarkm if(show_file(_PATH_ISSUE_NET, code) != 0) 76255682Smarkm show_file(_PATH_ISSUE, code); 76355682Smarkm if (guest) { 76455682Smarkm reply(code, "Guest login ok, access restrictions apply."); 76555682Smarkm#ifdef HAVE_SETPROCTITLE 76655682Smarkm snprintf (proctitle, sizeof(proctitle), 76755682Smarkm "%s: anonymous/%s", 76855682Smarkm remotehost, 76955682Smarkm passwd); 77064593Skris setproctitle("%s", proctitle); 77155682Smarkm#endif /* HAVE_SETPROCTITLE */ 77255682Smarkm if (logging) { 77355682Smarkm char data_addr[256]; 77455682Smarkm 77555682Smarkm if (inet_ntop (his_addr->sa_family, 77655682Smarkm socket_get_address(his_addr), 77755682Smarkm data_addr, sizeof(data_addr)) == NULL) 77855682Smarkm strlcpy (data_addr, "unknown address", 77955682Smarkm sizeof(data_addr)); 78055682Smarkm 78155682Smarkm syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s", 782233294Sstas remotehost, 78355682Smarkm data_addr, 78455682Smarkm passwd); 78555682Smarkm } 78655682Smarkm } else { 78755682Smarkm reply(code, "User %s logged in.", pw->pw_name); 78855682Smarkm#ifdef HAVE_SETPROCTITLE 78955682Smarkm snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); 79064593Skris setproctitle("%s", proctitle); 79155682Smarkm#endif /* HAVE_SETPROCTITLE */ 79255682Smarkm if (logging) { 79355682Smarkm char data_addr[256]; 79455682Smarkm 79555682Smarkm if (inet_ntop (his_addr->sa_family, 79655682Smarkm socket_get_address(his_addr), 79755682Smarkm data_addr, sizeof(data_addr)) == NULL) 79855682Smarkm strlcpy (data_addr, "unknown address", 79955682Smarkm sizeof(data_addr)); 80055682Smarkm 80155682Smarkm syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s", 80255682Smarkm remotehost, 80355682Smarkm data_addr, 80455682Smarkm pw->pw_name); 80555682Smarkm } 80655682Smarkm } 80755682Smarkm umask(defumask); 80855682Smarkm return 0; 80955682Smarkm} 81055682Smarkm 81155682Smarkm/* 81255682Smarkm * Terminate login as previous user, if any, resetting state; 81355682Smarkm * used when USER command is given or login fails. 81455682Smarkm */ 81555682Smarkmstatic void 81655682Smarkmend_login(void) 81755682Smarkm{ 81855682Smarkm 819178825Sdfr if (seteuid((uid_t)0) < 0) 820178825Sdfr fatal("Failed to seteuid"); 82155682Smarkm if (logged_in) 82255682Smarkm ftpd_logwtmp(ttyline, "", ""); 82355682Smarkm pw = NULL; 82455682Smarkm logged_in = 0; 82555682Smarkm guest = 0; 82655682Smarkm dochroot = 0; 82755682Smarkm} 82855682Smarkm 82972445Sassar#ifdef KRB5 83072445Sassarstatic int 83172445Sassarkrb5_verify(struct passwd *pwd, char *passwd) 83272445Sassar{ 833233294Sstas krb5_context context; 83472445Sassar krb5_ccache id; 83572445Sassar krb5_principal princ; 83672445Sassar krb5_error_code ret; 837233294Sstas 83872445Sassar ret = krb5_init_context(&context); 83972445Sassar if(ret) 84072445Sassar return ret; 84172445Sassar 84272445Sassar ret = krb5_parse_name(context, pwd->pw_name, &princ); 84372445Sassar if(ret){ 84472445Sassar krb5_free_context(context); 84572445Sassar return ret; 84672445Sassar } 847233294Sstas ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id); 84872445Sassar if(ret){ 84972445Sassar krb5_free_principal(context, princ); 85072445Sassar krb5_free_context(context); 85172445Sassar return ret; 85272445Sassar } 85372445Sassar ret = krb5_verify_user(context, 85472445Sassar princ, 85572445Sassar id, 85672445Sassar passwd, 85772445Sassar 1, 85872445Sassar NULL); 85972445Sassar krb5_free_principal(context, princ); 86072445Sassar if (k_hasafs()) { 86172445Sassar krb5_afslog_uid_home(context, id,NULL, NULL,pwd->pw_uid, pwd->pw_dir); 86272445Sassar } 86372445Sassar krb5_cc_destroy(context, id); 86472445Sassar krb5_free_context (context); 865233294Sstas if(ret) 86672445Sassar return ret; 86772445Sassar return 0; 86872445Sassar} 86972445Sassar#endif /* KRB5 */ 87072445Sassar 87155682Smarkmvoid 87255682Smarkmpass(char *passwd) 87355682Smarkm{ 87455682Smarkm int rval; 87555682Smarkm 87655682Smarkm /* some clients insists on sending a password */ 87755682Smarkm if (logged_in && askpasswd == 0){ 87872445Sassar reply(230, "Password not necessary"); 87972445Sassar return; 88055682Smarkm } 88155682Smarkm 88255682Smarkm if (logged_in || askpasswd == 0) { 88355682Smarkm reply(503, "Login with USER first."); 88455682Smarkm return; 88555682Smarkm } 88655682Smarkm askpasswd = 0; 88755682Smarkm rval = 1; 88855682Smarkm if (!guest) { /* "ftp" is only account allowed no password */ 88955682Smarkm if (pw == NULL) 89055682Smarkm rval = 1; /* failure below */ 89155682Smarkm#ifdef OTP 89255682Smarkm else if (otp_verify_user (&otp_ctx, passwd) == 0) { 89355682Smarkm rval = 0; 89455682Smarkm } 89555682Smarkm#endif 89655682Smarkm else if((auth_level & AUTH_OTP) == 0) { 89772445Sassar#ifdef KRB5 89872445Sassar rval = krb5_verify(pw, passwd); 89972445Sassar#endif 90072445Sassar if (rval) 90155682Smarkm rval = unix_verify_user(pw->pw_name, passwd); 90255682Smarkm } else { 903178825Sdfr#ifdef OTP 90455682Smarkm char *s; 90555682Smarkm if ((s = otp_error(&otp_ctx)) != NULL) 90655682Smarkm lreply(530, "OTP: %s", s); 90755682Smarkm#endif 90855682Smarkm } 90955682Smarkm memset (passwd, 0, strlen(passwd)); 91055682Smarkm 91155682Smarkm /* 91255682Smarkm * If rval == 1, the user failed the authentication 91355682Smarkm * check above. If rval == 0, either Kerberos or 91455682Smarkm * local authentication succeeded. 91555682Smarkm */ 91655682Smarkm if (rval) { 91755682Smarkm char data_addr[256]; 91855682Smarkm 91955682Smarkm if (inet_ntop (his_addr->sa_family, 92055682Smarkm socket_get_address(his_addr), 92155682Smarkm data_addr, sizeof(data_addr)) == NULL) 92255682Smarkm strlcpy (data_addr, "unknown address", 92355682Smarkm sizeof(data_addr)); 92455682Smarkm 92555682Smarkm reply(530, "Login incorrect."); 92655682Smarkm if (logging) 92755682Smarkm syslog(LOG_NOTICE, 92855682Smarkm "FTP LOGIN FAILED FROM %s(%s), %s", 92955682Smarkm remotehost, 93055682Smarkm data_addr, 93155682Smarkm curname); 93255682Smarkm pw = NULL; 93355682Smarkm if (login_attempts++ >= 5) { 93455682Smarkm syslog(LOG_NOTICE, 93555682Smarkm "repeated login failures from %s(%s)", 93655682Smarkm remotehost, 93755682Smarkm data_addr); 93855682Smarkm exit(0); 93955682Smarkm } 94055682Smarkm return; 94155682Smarkm } 94255682Smarkm } 94355682Smarkm if(!do_login(230, passwd)) 94455682Smarkm return; 945233294Sstas 94655682Smarkm /* Forget all about it... */ 94755682Smarkm end_login(); 94855682Smarkm} 94955682Smarkm 95055682Smarkmvoid 95155682Smarkmretrieve(const char *cmd, char *name) 95255682Smarkm{ 95355682Smarkm FILE *fin = NULL, *dout; 95455682Smarkm struct stat st; 95555682Smarkm int (*closefunc) (FILE *); 95655682Smarkm char line[BUFSIZ]; 95755682Smarkm 95855682Smarkm 95955682Smarkm if (cmd == 0) { 96055682Smarkm fin = fopen(name, "r"); 96155682Smarkm closefunc = fclose; 96255682Smarkm st.st_size = 0; 96355682Smarkm if(fin == NULL){ 96455682Smarkm int save_errno = errno; 96555682Smarkm struct cmds { 96655682Smarkm const char *ext; 96755682Smarkm const char *cmd; 96855682Smarkm const char *rev_cmd; 96955682Smarkm } cmds[] = { 97055682Smarkm {".tar", "/bin/gtar cPf - %s", NULL}, 97155682Smarkm {".tar.gz", "/bin/gtar zcPf - %s", NULL}, 97255682Smarkm {".tar.Z", "/bin/gtar ZcPf - %s", NULL}, 97355682Smarkm {".gz", "/bin/gzip -c -- %s", "/bin/gzip -c -d -- %s"}, 97455682Smarkm {".Z", "/bin/compress -c -- %s", "/bin/uncompress -c -- %s"}, 97555682Smarkm {NULL, NULL} 97655682Smarkm }; 97755682Smarkm struct cmds *p; 97855682Smarkm for(p = cmds; p->ext; p++){ 97955682Smarkm char *tail = name + strlen(name) - strlen(p->ext); 98055682Smarkm char c = *tail; 981233294Sstas 98255682Smarkm if(strcmp(tail, p->ext) == 0 && 98355682Smarkm (*tail = 0) == 0 && 98455682Smarkm access(name, R_OK) == 0){ 98555682Smarkm snprintf (line, sizeof(line), p->cmd, name); 98655682Smarkm *tail = c; 98755682Smarkm break; 98855682Smarkm } 98955682Smarkm *tail = c; 99055682Smarkm if (p->rev_cmd != NULL) { 99155682Smarkm char *ext; 992178825Sdfr int ret; 99355682Smarkm 994178825Sdfr ret = asprintf(&ext, "%s%s", name, p->ext); 995178825Sdfr if (ret != -1) { 99655682Smarkm if (access(ext, R_OK) == 0) { 99755682Smarkm snprintf (line, sizeof(line), 99855682Smarkm p->rev_cmd, ext); 99955682Smarkm free(ext); 100055682Smarkm break; 100155682Smarkm } 100255682Smarkm free(ext); 100355682Smarkm } 100455682Smarkm } 1005233294Sstas 100655682Smarkm } 100755682Smarkm if(p->ext){ 100855682Smarkm fin = ftpd_popen(line, "r", 0, 0); 100955682Smarkm closefunc = ftpd_pclose; 101055682Smarkm st.st_size = -1; 101155682Smarkm cmd = line; 101255682Smarkm } else 101355682Smarkm errno = save_errno; 101455682Smarkm } 101555682Smarkm } else { 101655682Smarkm snprintf(line, sizeof(line), cmd, name); 101755682Smarkm name = line; 101855682Smarkm fin = ftpd_popen(line, "r", 1, 0); 101955682Smarkm closefunc = ftpd_pclose; 102055682Smarkm st.st_size = -1; 102155682Smarkm } 102255682Smarkm if (fin == NULL) { 102355682Smarkm if (errno != 0) { 102455682Smarkm perror_reply(550, name); 102555682Smarkm if (cmd == 0) { 102655682Smarkm LOGCMD("get", name); 102755682Smarkm } 102855682Smarkm } 102955682Smarkm return; 103055682Smarkm } 103155682Smarkm byte_count = -1; 103255682Smarkm if (cmd == 0){ 103355682Smarkm if(fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { 103455682Smarkm reply(550, "%s: not a plain file.", name); 103555682Smarkm goto done; 103655682Smarkm } 103755682Smarkm } 103855682Smarkm if (restart_point) { 103955682Smarkm if (type == TYPE_A) { 104055682Smarkm off_t i, n; 104155682Smarkm int c; 104255682Smarkm 104355682Smarkm n = restart_point; 104455682Smarkm i = 0; 104555682Smarkm while (i++ < n) { 104655682Smarkm if ((c=getc(fin)) == EOF) { 104755682Smarkm perror_reply(550, name); 104855682Smarkm goto done; 104955682Smarkm } 105055682Smarkm if (c == '\n') 105155682Smarkm i++; 105255682Smarkm } 105355682Smarkm } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { 105455682Smarkm perror_reply(550, name); 105555682Smarkm goto done; 105655682Smarkm } 105755682Smarkm } 105855682Smarkm dout = dataconn(name, st.st_size, "w"); 105955682Smarkm if (dout == NULL) 106055682Smarkm goto done; 106155682Smarkm set_buffer_size(fileno(dout), 0); 106255682Smarkm send_data(fin, dout); 106355682Smarkm fclose(dout); 106455682Smarkm data = -1; 106555682Smarkm pdata = -1; 106655682Smarkmdone: 106755682Smarkm if (cmd == 0) 106855682Smarkm LOGBYTES("get", name, byte_count); 106955682Smarkm (*closefunc)(fin); 107055682Smarkm} 107155682Smarkm 107255682Smarkm/* filename sanity check */ 107355682Smarkm 1074233294Sstasint 107555682Smarkmfilename_check(char *filename) 107655682Smarkm{ 1077178825Sdfr char *p; 107855682Smarkm 1079178825Sdfr p = strrchr(filename, '/'); 108055682Smarkm if(p) 108155682Smarkm filename = p + 1; 108255682Smarkm 108355682Smarkm p = filename; 108455682Smarkm 1085178825Sdfr if(isalnum((unsigned char)*p)){ 108655682Smarkm p++; 1087178825Sdfr while(*p && (isalnum((unsigned char)*p) || strchr(good_chars, (unsigned char)*p))) 108855682Smarkm p++; 108955682Smarkm if(*p == '\0') 109055682Smarkm return 0; 109155682Smarkm } 109272445Sassar lreply(553, "\"%s\" is not an acceptable filename.", filename); 109355682Smarkm lreply(553, "The filename must start with an alphanumeric " 109455682Smarkm "character and must only"); 1095233294Sstas reply(553, "consist of alphanumeric characters or any of the following: %s", 109655682Smarkm good_chars); 109755682Smarkm return 1; 109855682Smarkm} 109955682Smarkm 110055682Smarkmvoid 110155682Smarkmdo_store(char *name, char *mode, int unique) 110255682Smarkm{ 110355682Smarkm FILE *fout, *din; 110455682Smarkm struct stat st; 110555682Smarkm int (*closefunc) (FILE *); 110655682Smarkm 110755682Smarkm if(guest && filename_check(name)) 110855682Smarkm return; 1109233294Sstas if (unique) { 1110233294Sstas char *uname; 1111233294Sstas if (stat(name, &st) == 0) { 1112233294Sstas if ((uname = gunique(name)) == NULL) 1113233294Sstas return; 1114233294Sstas name = uname; 1115233294Sstas } 1116233294Sstas LOGCMD(*mode == 'w' ? "put" : "append", name); 111755682Smarkm } 111855682Smarkm 111955682Smarkm if (restart_point) 112055682Smarkm mode = "r+"; 112155682Smarkm fout = fopen(name, mode); 112255682Smarkm closefunc = fclose; 112355682Smarkm if (fout == NULL) { 112455682Smarkm perror_reply(553, name); 112555682Smarkm LOGCMD(*mode == 'w' ? "put" : "append", name); 112655682Smarkm return; 112755682Smarkm } 112855682Smarkm byte_count = -1; 112955682Smarkm if (restart_point) { 113055682Smarkm if (type == TYPE_A) { 113155682Smarkm off_t i, n; 113255682Smarkm int c; 113355682Smarkm 113455682Smarkm n = restart_point; 113555682Smarkm i = 0; 113655682Smarkm while (i++ < n) { 113755682Smarkm if ((c=getc(fout)) == EOF) { 113855682Smarkm perror_reply(550, name); 113955682Smarkm goto done; 114055682Smarkm } 114155682Smarkm if (c == '\n') 114255682Smarkm i++; 114355682Smarkm } 114455682Smarkm /* 114555682Smarkm * We must do this seek to "current" position 114655682Smarkm * because we are changing from reading to 114755682Smarkm * writing. 114855682Smarkm */ 114955682Smarkm if (fseek(fout, 0L, SEEK_CUR) < 0) { 115055682Smarkm perror_reply(550, name); 115155682Smarkm goto done; 115255682Smarkm } 115355682Smarkm } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) { 115455682Smarkm perror_reply(550, name); 115555682Smarkm goto done; 115655682Smarkm } 115755682Smarkm } 115855682Smarkm din = dataconn(name, (off_t)-1, "r"); 115955682Smarkm if (din == NULL) 116055682Smarkm goto done; 116155682Smarkm set_buffer_size(fileno(din), 1); 116255682Smarkm if (receive_data(din, fout) == 0) { 116378527Sassar if((*closefunc)(fout) < 0) 116478527Sassar perror_reply(552, name); 116578527Sassar else { 116655682Smarkm if (unique) 116755682Smarkm reply(226, "Transfer complete (unique file name:%s).", 116855682Smarkm name); 116955682Smarkm else 117055682Smarkm reply(226, "Transfer complete."); 117178527Sassar } 117278527Sassar } else 117378527Sassar (*closefunc)(fout); 117455682Smarkm fclose(din); 117555682Smarkm data = -1; 117655682Smarkm pdata = -1; 117755682Smarkmdone: 117855682Smarkm LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 117955682Smarkm} 118055682Smarkm 118155682Smarkmstatic FILE * 1182178825Sdfrgetdatasock(const char *mode, int domain) 118355682Smarkm{ 118455682Smarkm int s, t, tries; 118555682Smarkm 118655682Smarkm if (data >= 0) 118755682Smarkm return (fdopen(data, mode)); 1188178825Sdfr if (seteuid(0) < 0) 1189178825Sdfr fatal("Failed to seteuid"); 1190178825Sdfr s = socket(domain, SOCK_STREAM, 0); 119155682Smarkm if (s < 0) 119255682Smarkm goto bad; 119355682Smarkm socket_set_reuseaddr (s, 1); 119455682Smarkm /* anchor socket to avoid multi-homing problems */ 119555682Smarkm socket_set_address_and_port (data_source, 119655682Smarkm socket_get_address (ctrl_addr), 119755682Smarkm socket_get_port (data_source)); 119855682Smarkm 119955682Smarkm for (tries = 1; ; tries++) { 120055682Smarkm if (bind(s, data_source, 120155682Smarkm socket_sockaddr_size (data_source)) >= 0) 120255682Smarkm break; 120355682Smarkm if (errno != EADDRINUSE || tries > 10) 120455682Smarkm goto bad; 120555682Smarkm sleep(tries); 120655682Smarkm } 1207178825Sdfr if (seteuid(pw->pw_uid) < 0) 1208178825Sdfr fatal("Failed to seteuid"); 120955682Smarkm#ifdef IPTOS_THROUGHPUT 121055682Smarkm socket_set_tos (s, IPTOS_THROUGHPUT); 121155682Smarkm#endif 121255682Smarkm return (fdopen(s, mode)); 121355682Smarkmbad: 121455682Smarkm /* Return the real value of errno (close may change it) */ 121555682Smarkm t = errno; 1216178825Sdfr if (seteuid((uid_t)pw->pw_uid) < 0) 1217178825Sdfr fatal("Failed to seteuid"); 121855682Smarkm close(s); 121955682Smarkm errno = t; 122055682Smarkm return (NULL); 122155682Smarkm} 122255682Smarkm 122390926Snectarstatic int 1224233294Sstasaccept_with_timeout(int socket, 122590926Snectar struct sockaddr *address, 1226120945Snectar socklen_t *address_len, 122790926Snectar struct timeval *timeout) 122890926Snectar{ 122990926Snectar int ret; 123090926Snectar fd_set rfd; 123190926Snectar FD_ZERO(&rfd); 123290926Snectar FD_SET(socket, &rfd); 123390926Snectar ret = select(socket + 1, &rfd, NULL, NULL, timeout); 123490926Snectar if(ret < 0) 123590926Snectar return ret; 123690926Snectar if(ret == 0) { 123790926Snectar errno = ETIMEDOUT; 123890926Snectar return -1; 123990926Snectar } 124090926Snectar return accept(socket, address, address_len); 124190926Snectar} 124290926Snectar 124355682Smarkmstatic FILE * 124455682Smarkmdataconn(const char *name, off_t size, const char *mode) 124555682Smarkm{ 124655682Smarkm char sizebuf[32]; 124755682Smarkm FILE *file; 1248178825Sdfr int domain, retry = 0; 124955682Smarkm 125055682Smarkm file_size = size; 125155682Smarkm byte_count = 0; 125255682Smarkm if (size >= 0) 125355682Smarkm snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", (long)size); 125455682Smarkm else 125555682Smarkm *sizebuf = '\0'; 125655682Smarkm if (pdata >= 0) { 125755682Smarkm struct sockaddr_storage from_ss; 125855682Smarkm struct sockaddr *from = (struct sockaddr *)&from_ss; 125990926Snectar struct timeval timeout; 126055682Smarkm int s; 126172445Sassar socklen_t fromlen = sizeof(from_ss); 126255682Smarkm 126390926Snectar timeout.tv_sec = 15; 126490926Snectar timeout.tv_usec = 0; 126590926Snectar s = accept_with_timeout(pdata, from, &fromlen, &timeout); 126655682Smarkm if (s < 0) { 126755682Smarkm reply(425, "Can't open data connection."); 126855682Smarkm close(pdata); 126955682Smarkm pdata = -1; 127055682Smarkm return (NULL); 127155682Smarkm } 127255682Smarkm close(pdata); 127355682Smarkm pdata = s; 1274233294Sstas#if defined(IPTOS_THROUGHPUT) 1275233294Sstas if (from->sa_family == AF_INET) 1276233294Sstas socket_set_tos(s, IPTOS_THROUGHPUT); 127755682Smarkm#endif 127855682Smarkm reply(150, "Opening %s mode data connection for '%s'%s.", 127955682Smarkm type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 128055682Smarkm return (fdopen(pdata, mode)); 128155682Smarkm } 128255682Smarkm if (data >= 0) { 128355682Smarkm reply(125, "Using existing data connection for '%s'%s.", 128455682Smarkm name, sizebuf); 128555682Smarkm usedefault = 1; 128655682Smarkm return (fdopen(data, mode)); 128755682Smarkm } 128855682Smarkm if (usedefault) 128955682Smarkm data_dest = his_addr; 129055682Smarkm usedefault = 1; 1291233294Sstas /* 1292178825Sdfr * Default to using the same socket type as the ctrl address, 1293178825Sdfr * unless we know the type of the data address. 1294178825Sdfr */ 1295178825Sdfr domain = data_dest->sa_family; 1296178825Sdfr if (domain == PF_UNSPEC) 1297178825Sdfr domain = ctrl_addr->sa_family; 1298178825Sdfr 1299178825Sdfr file = getdatasock(mode, domain); 130055682Smarkm if (file == NULL) { 130155682Smarkm char data_addr[256]; 130255682Smarkm 130355682Smarkm if (inet_ntop (data_source->sa_family, 130455682Smarkm socket_get_address(data_source), 130555682Smarkm data_addr, sizeof(data_addr)) == NULL) 130655682Smarkm strlcpy (data_addr, "unknown address", 130755682Smarkm sizeof(data_addr)); 130855682Smarkm 130955682Smarkm reply(425, "Can't create data socket (%s,%d): %s.", 131055682Smarkm data_addr, 131155682Smarkm socket_get_port (data_source), 131255682Smarkm strerror(errno)); 131355682Smarkm return (NULL); 131455682Smarkm } 131555682Smarkm data = fileno(file); 131655682Smarkm while (connect(data, data_dest, 131755682Smarkm socket_sockaddr_size(data_dest)) < 0) { 131855682Smarkm if (errno == EADDRINUSE && retry < swaitmax) { 131955682Smarkm sleep(swaitint); 132055682Smarkm retry += swaitint; 132155682Smarkm continue; 132255682Smarkm } 132355682Smarkm perror_reply(425, "Can't build data connection"); 132455682Smarkm fclose(file); 132555682Smarkm data = -1; 132655682Smarkm return (NULL); 132755682Smarkm } 132855682Smarkm reply(150, "Opening %s mode data connection for '%s'%s.", 132955682Smarkm type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 133055682Smarkm return (file); 133155682Smarkm} 133255682Smarkm 133355682Smarkm/* 133455682Smarkm * Tranfer the contents of "instr" to "outstr" peer using the appropriate 133555682Smarkm * encapsulation of the data subject * to Mode, Structure, and Type. 133655682Smarkm * 133755682Smarkm * NB: Form isn't handled. 133855682Smarkm */ 133955682Smarkmstatic void 134055682Smarkmsend_data(FILE *instr, FILE *outstr) 134155682Smarkm{ 134255682Smarkm int c, cnt, filefd, netfd; 134355682Smarkm static char *buf; 134455682Smarkm static size_t bufsize; 134555682Smarkm 1346142403Snectar transflag = 1; 134755682Smarkm switch (type) { 134855682Smarkm 134955682Smarkm case TYPE_A: 135055682Smarkm while ((c = getc(instr)) != EOF) { 1351142403Snectar if (urgflag && handleoobcmd()) 1352142403Snectar return; 135355682Smarkm byte_count++; 135455682Smarkm if(c == '\n') 135555682Smarkm sec_putc('\r', outstr); 135655682Smarkm sec_putc(c, outstr); 135755682Smarkm } 135855682Smarkm sec_fflush(outstr); 135955682Smarkm transflag = 0; 1360142403Snectar urgflag = 0; 136155682Smarkm if (ferror(instr)) 136255682Smarkm goto file_err; 136355682Smarkm if (ferror(outstr)) 136455682Smarkm goto data_err; 136555682Smarkm reply(226, "Transfer complete."); 136655682Smarkm return; 1367233294Sstas 136855682Smarkm case TYPE_I: 136955682Smarkm case TYPE_L: 1370142403Snectar#if 0 /* XXX handle urg flag */ 137155682Smarkm#if defined(HAVE_MMAP) && !defined(NO_MMAP) 137255682Smarkm#ifndef MAP_FAILED 137355682Smarkm#define MAP_FAILED (-1) 137455682Smarkm#endif 137555682Smarkm { 137655682Smarkm struct stat st; 137755682Smarkm char *chunk; 137855682Smarkm int in = fileno(instr); 1379233294Sstas if(fstat(in, &st) == 0 && S_ISREG(st.st_mode) 138055682Smarkm && st.st_size > 0) { 138155682Smarkm /* 138255682Smarkm * mmap zero bytes has potential of loosing, don't do it. 138355682Smarkm */ 138455682Smarkm chunk = mmap(0, st.st_size, PROT_READ, 138555682Smarkm MAP_SHARED, in, 0); 138655682Smarkm if((void *)chunk != (void *)MAP_FAILED) { 138755682Smarkm cnt = st.st_size - restart_point; 138855682Smarkm sec_write(fileno(outstr), chunk + restart_point, cnt); 138955682Smarkm if (munmap(chunk, st.st_size) < 0) 139055682Smarkm warn ("munmap"); 139155682Smarkm sec_fflush(outstr); 139255682Smarkm byte_count = cnt; 139355682Smarkm transflag = 0; 1394142403Snectar urgflag = 0; 139555682Smarkm } 139655682Smarkm } 139755682Smarkm } 139855682Smarkm#endif 1399142403Snectar#endif 140055682Smarkm if(transflag) { 140155682Smarkm struct stat st; 140255682Smarkm 140355682Smarkm netfd = fileno(outstr); 140455682Smarkm filefd = fileno(instr); 140555682Smarkm buf = alloc_buffer (buf, &bufsize, 140655682Smarkm fstat(filefd, &st) >= 0 ? &st : NULL); 140755682Smarkm if (buf == NULL) { 140855682Smarkm transflag = 0; 1409142403Snectar urgflag = 0; 141055682Smarkm perror_reply(451, "Local resource failure: malloc"); 141155682Smarkm return; 141255682Smarkm } 141355682Smarkm while ((cnt = read(filefd, buf, bufsize)) > 0 && 1414142403Snectar sec_write(netfd, buf, cnt) == cnt) { 141555682Smarkm byte_count += cnt; 1416142403Snectar if (urgflag && handleoobcmd()) 1417142403Snectar return; 1418142403Snectar } 141955682Smarkm sec_fflush(outstr); /* to end an encrypted stream */ 142055682Smarkm transflag = 0; 1421142403Snectar urgflag = 0; 142255682Smarkm if (cnt != 0) { 142355682Smarkm if (cnt < 0) 142455682Smarkm goto file_err; 142555682Smarkm goto data_err; 142655682Smarkm } 142755682Smarkm } 142855682Smarkm reply(226, "Transfer complete."); 142955682Smarkm return; 143055682Smarkm default: 143155682Smarkm transflag = 0; 1432142403Snectar urgflag = 0; 143355682Smarkm reply(550, "Unimplemented TYPE %d in send_data", type); 143455682Smarkm return; 143555682Smarkm } 143655682Smarkm 143755682Smarkmdata_err: 143855682Smarkm transflag = 0; 1439142403Snectar urgflag = 0; 144055682Smarkm perror_reply(426, "Data connection"); 144155682Smarkm return; 144255682Smarkm 144355682Smarkmfile_err: 144455682Smarkm transflag = 0; 1445142403Snectar urgflag = 0; 144655682Smarkm perror_reply(551, "Error on input file"); 144755682Smarkm} 144855682Smarkm 144955682Smarkm/* 145055682Smarkm * Transfer data from peer to "outstr" using the appropriate encapulation of 145155682Smarkm * the data subject to Mode, Structure, and Type. 145255682Smarkm * 145355682Smarkm * N.B.: Form isn't handled. 145455682Smarkm */ 145555682Smarkmstatic int 145655682Smarkmreceive_data(FILE *instr, FILE *outstr) 145755682Smarkm{ 145855682Smarkm int cnt, bare_lfs = 0; 145955682Smarkm static char *buf; 146055682Smarkm static size_t bufsize; 146155682Smarkm struct stat st; 146255682Smarkm 1463142403Snectar transflag = 1; 146455682Smarkm 146555682Smarkm buf = alloc_buffer (buf, &bufsize, 146655682Smarkm fstat(fileno(outstr), &st) >= 0 ? &st : NULL); 146755682Smarkm if (buf == NULL) { 146855682Smarkm transflag = 0; 1469142403Snectar urgflag = 0; 147055682Smarkm perror_reply(451, "Local resource failure: malloc"); 147155682Smarkm return -1; 147255682Smarkm } 1473233294Sstas 147455682Smarkm switch (type) { 147555682Smarkm 147655682Smarkm case TYPE_I: 147755682Smarkm case TYPE_L: 147855682Smarkm while ((cnt = sec_read(fileno(instr), buf, bufsize)) > 0) { 147955682Smarkm if (write(fileno(outstr), buf, cnt) != cnt) 148055682Smarkm goto file_err; 148155682Smarkm byte_count += cnt; 1482142403Snectar if (urgflag && handleoobcmd()) 1483142403Snectar return (-1); 148455682Smarkm } 148555682Smarkm if (cnt < 0) 148655682Smarkm goto data_err; 148755682Smarkm transflag = 0; 1488142403Snectar urgflag = 0; 148955682Smarkm return (0); 149055682Smarkm 149155682Smarkm case TYPE_E: 149255682Smarkm reply(553, "TYPE E not implemented."); 149355682Smarkm transflag = 0; 1494142403Snectar urgflag = 0; 149555682Smarkm return (-1); 149655682Smarkm 149755682Smarkm case TYPE_A: 149855682Smarkm { 149955682Smarkm char *p, *q; 150055682Smarkm int cr_flag = 0; 150155682Smarkm while ((cnt = sec_read(fileno(instr), 1502233294Sstas buf + cr_flag, 150355682Smarkm bufsize - cr_flag)) > 0){ 1504142403Snectar if (urgflag && handleoobcmd()) 1505142403Snectar return (-1); 150655682Smarkm byte_count += cnt; 150755682Smarkm cnt += cr_flag; 150855682Smarkm cr_flag = 0; 150955682Smarkm for(p = buf, q = buf; p < buf + cnt;) { 151055682Smarkm if(*p == '\n') 151155682Smarkm bare_lfs++; 151255682Smarkm if(*p == '\r') { 151355682Smarkm if(p == buf + cnt - 1){ 151455682Smarkm cr_flag = 1; 151555682Smarkm p++; 151655682Smarkm continue; 151755682Smarkm }else if(p[1] == '\n'){ 151855682Smarkm *q++ = '\n'; 151955682Smarkm p += 2; 152055682Smarkm continue; 152155682Smarkm } 152255682Smarkm } 152355682Smarkm *q++ = *p++; 152455682Smarkm } 152555682Smarkm fwrite(buf, q - buf, 1, outstr); 152655682Smarkm if(cr_flag) 152755682Smarkm buf[0] = '\r'; 152855682Smarkm } 152955682Smarkm if(cr_flag) 153055682Smarkm putc('\r', outstr); 153155682Smarkm fflush(outstr); 153255682Smarkm if (ferror(instr)) 153355682Smarkm goto data_err; 153455682Smarkm if (ferror(outstr)) 153555682Smarkm goto file_err; 153655682Smarkm transflag = 0; 1537142403Snectar urgflag = 0; 153855682Smarkm if (bare_lfs) { 153955682Smarkm lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n" 154055682Smarkm " File may not have transferred correctly.\r\n", 154155682Smarkm bare_lfs); 154255682Smarkm } 154355682Smarkm return (0); 154455682Smarkm } 154555682Smarkm default: 154655682Smarkm reply(550, "Unimplemented TYPE %d in receive_data", type); 154755682Smarkm transflag = 0; 1548142403Snectar urgflag = 0; 154955682Smarkm return (-1); 155055682Smarkm } 1551233294Sstas 155255682Smarkmdata_err: 155355682Smarkm transflag = 0; 1554142403Snectar urgflag = 0; 155555682Smarkm perror_reply(426, "Data Connection"); 155655682Smarkm return (-1); 1557233294Sstas 155855682Smarkmfile_err: 155955682Smarkm transflag = 0; 1560142403Snectar urgflag = 0; 156155682Smarkm perror_reply(452, "Error writing file"); 156255682Smarkm return (-1); 156355682Smarkm} 156455682Smarkm 156555682Smarkmvoid 156655682Smarkmstatfilecmd(char *filename) 156755682Smarkm{ 156855682Smarkm FILE *fin; 156955682Smarkm int c; 157055682Smarkm char line[LINE_MAX]; 157155682Smarkm 157255682Smarkm snprintf(line, sizeof(line), "/bin/ls -la -- %s", filename); 157355682Smarkm fin = ftpd_popen(line, "r", 1, 0); 157455682Smarkm lreply(211, "status of %s:", filename); 157555682Smarkm while ((c = getc(fin)) != EOF) { 157655682Smarkm if (c == '\n') { 157755682Smarkm if (ferror(stdout)){ 157855682Smarkm perror_reply(421, "control connection"); 157955682Smarkm ftpd_pclose(fin); 158055682Smarkm dologout(1); 158155682Smarkm /* NOTREACHED */ 158255682Smarkm } 158355682Smarkm if (ferror(fin)) { 158455682Smarkm perror_reply(551, filename); 158555682Smarkm ftpd_pclose(fin); 158655682Smarkm return; 158755682Smarkm } 158855682Smarkm putc('\r', stdout); 158955682Smarkm } 159055682Smarkm putc(c, stdout); 159155682Smarkm } 159255682Smarkm ftpd_pclose(fin); 159355682Smarkm reply(211, "End of Status"); 159455682Smarkm} 159555682Smarkm 159655682Smarkmvoid 159755682Smarkmstatcmd(void) 159855682Smarkm{ 159955682Smarkm#if 0 160055682Smarkm struct sockaddr_in *sin; 160155682Smarkm u_char *a, *p; 160255682Smarkm 160355682Smarkm lreply(211, "%s FTP server (%s) status:", hostname, version); 160455682Smarkm printf(" %s\r\n", version); 160555682Smarkm printf(" Connected to %s", remotehost); 1606178825Sdfr if (!isdigit((unsigned char)remotehost[0])) 160755682Smarkm printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 160855682Smarkm printf("\r\n"); 160955682Smarkm if (logged_in) { 161055682Smarkm if (guest) 161155682Smarkm printf(" Logged in anonymously\r\n"); 161255682Smarkm else 161355682Smarkm printf(" Logged in as %s\r\n", pw->pw_name); 161455682Smarkm } else if (askpasswd) 161555682Smarkm printf(" Waiting for password\r\n"); 161655682Smarkm else 161755682Smarkm printf(" Waiting for user name\r\n"); 161855682Smarkm printf(" TYPE: %s", typenames[type]); 161955682Smarkm if (type == TYPE_A || type == TYPE_E) 162055682Smarkm printf(", FORM: %s", formnames[form]); 162155682Smarkm if (type == TYPE_L) 162255682Smarkm#if NBBY == 8 162355682Smarkm printf(" %d", NBBY); 162455682Smarkm#else 162555682Smarkm printf(" %d", bytesize); /* need definition! */ 162655682Smarkm#endif 162755682Smarkm printf("; STRUcture: %s; transfer MODE: %s\r\n", 162855682Smarkm strunames[stru], modenames[mode]); 162955682Smarkm if (data != -1) 163055682Smarkm printf(" Data connection open\r\n"); 163155682Smarkm else if (pdata != -1) { 163255682Smarkm printf(" in Passive mode"); 163355682Smarkm sin = &pasv_addr; 163455682Smarkm goto printaddr; 163555682Smarkm } else if (usedefault == 0) { 163655682Smarkm printf(" PORT"); 163755682Smarkm sin = &data_dest; 163855682Smarkmprintaddr: 163955682Smarkm a = (u_char *) &sin->sin_addr; 164055682Smarkm p = (u_char *) &sin->sin_port; 164155682Smarkm#define UC(b) (((int) b) & 0xff) 164255682Smarkm printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 164355682Smarkm UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 164455682Smarkm#undef UC 164555682Smarkm } else 164655682Smarkm printf(" No data connection\r\n"); 164755682Smarkm#endif 164855682Smarkm reply(211, "End of status"); 164955682Smarkm} 165055682Smarkm 165155682Smarkmvoid 165255682Smarkmfatal(char *s) 165355682Smarkm{ 165455682Smarkm 165555682Smarkm reply(451, "Error in server: %s\n", s); 165655682Smarkm reply(221, "Closing connection due to server error."); 165755682Smarkm dologout(0); 165855682Smarkm /* NOTREACHED */ 165955682Smarkm} 166055682Smarkm 166155682Smarkmstatic void 166255682Smarkmint_reply(int, char *, const char *, va_list) 166355682Smarkm#ifdef __GNUC__ 166455682Smarkm__attribute__ ((format (printf, 3, 0))) 166555682Smarkm#endif 166655682Smarkm; 166755682Smarkm 166855682Smarkmstatic void 166955682Smarkmint_reply(int n, char *c, const char *fmt, va_list ap) 167055682Smarkm{ 167155682Smarkm char buf[10240]; 167255682Smarkm char *p; 167355682Smarkm p=buf; 167455682Smarkm if(n){ 167555682Smarkm snprintf(p, sizeof(buf), "%d%s", n, c); 167655682Smarkm p+=strlen(p); 167755682Smarkm } 167855682Smarkm vsnprintf(p, sizeof(buf) - strlen(p), fmt, ap); 167955682Smarkm p+=strlen(p); 168055682Smarkm snprintf(p, sizeof(buf) - strlen(p), "\r\n"); 168155682Smarkm p+=strlen(p); 168255682Smarkm sec_fprintf(stdout, "%s", buf); 168355682Smarkm fflush(stdout); 168455682Smarkm if (debug) 168555682Smarkm syslog(LOG_DEBUG, "<--- %s- ", buf); 168655682Smarkm} 168755682Smarkm 168855682Smarkmvoid 168955682Smarkmreply(int n, const char *fmt, ...) 169055682Smarkm{ 169155682Smarkm va_list ap; 169255682Smarkm va_start(ap, fmt); 169355682Smarkm int_reply(n, " ", fmt, ap); 169455682Smarkm delete_ftp_command(); 169555682Smarkm va_end(ap); 169655682Smarkm} 169755682Smarkm 169855682Smarkmvoid 169955682Smarkmlreply(int n, const char *fmt, ...) 170055682Smarkm{ 170155682Smarkm va_list ap; 170255682Smarkm va_start(ap, fmt); 170355682Smarkm int_reply(n, "-", fmt, ap); 170455682Smarkm va_end(ap); 170555682Smarkm} 170655682Smarkm 170755682Smarkmvoid 170855682Smarkmnreply(const char *fmt, ...) 170955682Smarkm{ 171055682Smarkm va_list ap; 171155682Smarkm va_start(ap, fmt); 171255682Smarkm int_reply(0, NULL, fmt, ap); 171355682Smarkm va_end(ap); 171455682Smarkm} 171555682Smarkm 171655682Smarkmstatic void 171755682Smarkmack(char *s) 171855682Smarkm{ 171955682Smarkm 172055682Smarkm reply(250, "%s command successful.", s); 172155682Smarkm} 172255682Smarkm 172355682Smarkmvoid 172455682Smarkmnack(char *s) 172555682Smarkm{ 172655682Smarkm 172755682Smarkm reply(502, "%s command not implemented.", s); 172855682Smarkm} 172955682Smarkm 173055682Smarkmvoid 173155682Smarkmdo_delete(char *name) 173255682Smarkm{ 173355682Smarkm struct stat st; 173455682Smarkm 173555682Smarkm LOGCMD("delete", name); 173655682Smarkm if (stat(name, &st) < 0) { 173755682Smarkm perror_reply(550, name); 173855682Smarkm return; 173955682Smarkm } 1740233294Sstas if (S_ISDIR(st.st_mode)) { 174155682Smarkm if (rmdir(name) < 0) { 174255682Smarkm perror_reply(550, name); 174355682Smarkm return; 174455682Smarkm } 174555682Smarkm goto done; 174655682Smarkm } 174755682Smarkm if (unlink(name) < 0) { 174855682Smarkm perror_reply(550, name); 174955682Smarkm return; 175055682Smarkm } 175155682Smarkmdone: 175255682Smarkm ack("DELE"); 175355682Smarkm} 175455682Smarkm 175555682Smarkmvoid 1756233294Sstascwd(const char *path) 175755682Smarkm{ 175855682Smarkm 175955682Smarkm if (chdir(path) < 0) 176055682Smarkm perror_reply(550, path); 176155682Smarkm else 176255682Smarkm ack("CWD"); 176355682Smarkm} 176455682Smarkm 176555682Smarkmvoid 176655682Smarkmmakedir(char *name) 176755682Smarkm{ 176855682Smarkm 176955682Smarkm LOGCMD("mkdir", name); 177055682Smarkm if(guest && filename_check(name)) 177155682Smarkm return; 177255682Smarkm if (mkdir(name, 0777) < 0) 177355682Smarkm perror_reply(550, name); 177455682Smarkm else{ 177555682Smarkm if(guest) 177655682Smarkm chmod(name, 0700); /* guest has umask 777 */ 177755682Smarkm reply(257, "MKD command successful."); 177855682Smarkm } 177955682Smarkm} 178055682Smarkm 178155682Smarkmvoid 178255682Smarkmremovedir(char *name) 178355682Smarkm{ 178455682Smarkm 178555682Smarkm LOGCMD("rmdir", name); 178655682Smarkm if (rmdir(name) < 0) 178755682Smarkm perror_reply(550, name); 178855682Smarkm else 178955682Smarkm ack("RMD"); 179055682Smarkm} 179155682Smarkm 179255682Smarkmvoid 179355682Smarkmpwd(void) 179455682Smarkm{ 179555682Smarkm char path[MaxPathLen]; 179655682Smarkm char *ret; 179755682Smarkm 179855682Smarkm /* SunOS has a broken getcwd that does popen(pwd) (!!!), this 1799233294Sstas * failes miserably when running chroot 180055682Smarkm */ 180155682Smarkm ret = getcwd(path, sizeof(path)); 180255682Smarkm if (ret == NULL) 180355682Smarkm reply(550, "%s.", strerror(errno)); 180455682Smarkm else 180555682Smarkm reply(257, "\"%s\" is current directory.", path); 180655682Smarkm} 180755682Smarkm 180855682Smarkmchar * 180955682Smarkmrenamefrom(char *name) 181055682Smarkm{ 181155682Smarkm struct stat st; 181255682Smarkm 181355682Smarkm if (stat(name, &st) < 0) { 181455682Smarkm perror_reply(550, name); 181555682Smarkm return NULL; 181655682Smarkm } 181755682Smarkm reply(350, "File exists, ready for destination name"); 181855682Smarkm return (name); 181955682Smarkm} 182055682Smarkm 182155682Smarkmvoid 182255682Smarkmrenamecmd(char *from, char *to) 182355682Smarkm{ 182455682Smarkm 182555682Smarkm LOGCMD2("rename", from, to); 182655682Smarkm if(guest && filename_check(to)) 182755682Smarkm return; 182855682Smarkm if (rename(from, to) < 0) 182955682Smarkm perror_reply(550, "rename"); 183055682Smarkm else 183155682Smarkm ack("RNTO"); 183255682Smarkm} 183355682Smarkm 183455682Smarkmstatic void 183555682Smarkmdolog(struct sockaddr *sa, int len) 183655682Smarkm{ 183755682Smarkm getnameinfo_verified (sa, len, remotehost, sizeof(remotehost), 183855682Smarkm NULL, 0, 0); 183955682Smarkm#ifdef HAVE_SETPROCTITLE 184055682Smarkm snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 184164593Skris setproctitle("%s", proctitle); 184255682Smarkm#endif /* HAVE_SETPROCTITLE */ 184355682Smarkm 184455682Smarkm if (logging) { 184555682Smarkm char data_addr[256]; 184655682Smarkm 184755682Smarkm if (inet_ntop (his_addr->sa_family, 184855682Smarkm socket_get_address(his_addr), 184955682Smarkm data_addr, sizeof(data_addr)) == NULL) 185055682Smarkm strlcpy (data_addr, "unknown address", 185155682Smarkm sizeof(data_addr)); 185255682Smarkm 185355682Smarkm 185455682Smarkm syslog(LOG_INFO, "connection from %s(%s)", 185555682Smarkm remotehost, 185655682Smarkm data_addr); 185755682Smarkm } 185855682Smarkm} 185955682Smarkm 186055682Smarkm/* 186155682Smarkm * Record logout in wtmp file 186255682Smarkm * and exit with supplied status. 186355682Smarkm */ 186455682Smarkmvoid 186555682Smarkmdologout(int status) 186655682Smarkm{ 186755682Smarkm transflag = 0; 1868142403Snectar urgflag = 0; 186955682Smarkm if (logged_in) { 1870233294Sstas#if KRB5 187155682Smarkm cond_kdestroy(); 187255682Smarkm#endif 1873178825Sdfr seteuid((uid_t)0); /* No need to check, we call exit() below */ 1874178825Sdfr ftpd_logwtmp(ttyline, "", ""); 187555682Smarkm } 187655682Smarkm /* beware of flushing buffers after a SIGPIPE */ 187755682Smarkm#ifdef XXX 187855682Smarkm exit(status); 187955682Smarkm#else 188055682Smarkm _exit(status); 1881233294Sstas#endif 188255682Smarkm} 188355682Smarkm 188455682Smarkmvoid abor(void) 188555682Smarkm{ 1886142403Snectar if (!transflag) 1887142403Snectar return; 1888142403Snectar reply(426, "Transfer aborted. Data connection closed."); 1889142403Snectar reply(226, "Abort successful"); 1890142403Snectar transflag = 0; 189155682Smarkm} 189255682Smarkm 189355682Smarkmstatic void 189455682Smarkmmyoob(int signo) 189555682Smarkm{ 1896142403Snectar urgflag = 1; 1897142403Snectar} 1898142403Snectar 1899142403Snectarstatic char * 1900142403Snectarmec_space(char *p) 1901142403Snectar{ 1902142403Snectar while(isspace(*(unsigned char *)p)) 1903142403Snectar p++; 1904142403Snectar return p; 1905142403Snectar} 1906142403Snectar 1907142403Snectarstatic int 1908142403Snectarhandleoobcmd(void) 1909142403Snectar{ 191055682Smarkm char *cp; 191155682Smarkm 191255682Smarkm /* only process if transfer occurring */ 191355682Smarkm if (!transflag) 1914142403Snectar return 0; 191555682Smarkm 1916142403Snectar urgflag = 0; 191755682Smarkm 191855682Smarkm cp = tmpline; 1919142403Snectar if (ftpd_getline(cp, sizeof(tmpline)) == NULL) { 192055682Smarkm reply(221, "You could at least say goodbye."); 192155682Smarkm dologout(0); 192255682Smarkm } 1923142403Snectar 1924142403Snectar if (strncasecmp("MIC", cp, 3) == 0) { 1925142403Snectar mec(mec_space(cp + 3), prot_safe); 1926142403Snectar } else if (strncasecmp("CONF", cp, 4) == 0) { 1927142403Snectar mec(mec_space(cp + 4), prot_confidential); 1928142403Snectar } else if (strncasecmp("ENC", cp, 3) == 0) { 1929142403Snectar mec(mec_space(cp + 3), prot_private); 1930142403Snectar } else if (!allow_insecure_oob) { 1931142403Snectar reply(533, "Command protection level denied " 1932142403Snectar "for paranoid reasons."); 1933142403Snectar goto out; 193455682Smarkm } 1935142403Snectar 1936142403Snectar if (secure_command()) 1937142403Snectar cp = ftp_command; 1938142403Snectar 1939142403Snectar if (strcasecmp(cp, "ABOR\r\n") == 0) { 1940142403Snectar abor(); 1941142403Snectar } else if (strcasecmp(cp, "STAT\r\n") == 0) { 194255682Smarkm if (file_size != (off_t) -1) 194355682Smarkm reply(213, "Status: %ld of %ld bytes transferred", 194455682Smarkm (long)byte_count, 194555682Smarkm (long)file_size); 194655682Smarkm else 1947142403Snectar reply(213, "Status: %ld bytes transferred", 194855682Smarkm (long)byte_count); 194955682Smarkm } 1950142403Snectarout: 1951142403Snectar return (transflag == 0); 195255682Smarkm} 195355682Smarkm 195455682Smarkm/* 195555682Smarkm * Note: a response of 425 is not mentioned as a possible response to 195655682Smarkm * the PASV command in RFC959. However, it has been blessed as 195755682Smarkm * a legitimate response by Jon Postel in a telephone conversation 195855682Smarkm * with Rick Adams on 25 Jan 89. 195955682Smarkm */ 196055682Smarkmvoid 196155682Smarkmpasv(void) 196255682Smarkm{ 196372445Sassar socklen_t len; 196455682Smarkm char *p, *a; 196555682Smarkm struct sockaddr_in *sin; 196655682Smarkm 196755682Smarkm if (ctrl_addr->sa_family != AF_INET) { 196855682Smarkm reply(425, 196955682Smarkm "You cannot do PASV with something that's not IPv4"); 197055682Smarkm return; 197155682Smarkm } 197255682Smarkm 197372445Sassar if(pdata != -1) 197472445Sassar close(pdata); 197572445Sassar 197655682Smarkm pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0); 197755682Smarkm if (pdata < 0) { 197855682Smarkm perror_reply(425, "Can't open passive connection"); 197955682Smarkm return; 198055682Smarkm } 198155682Smarkm pasv_addr->sa_family = ctrl_addr->sa_family; 198255682Smarkm socket_set_address_and_port (pasv_addr, 198355682Smarkm socket_get_address (ctrl_addr), 198455682Smarkm 0); 1985233294Sstas socket_set_portrange(pdata, restricted_data_ports, 1986233294Sstas pasv_addr->sa_family); 1987178825Sdfr if (seteuid(0) < 0) 1988178825Sdfr fatal("Failed to seteuid"); 198955682Smarkm if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) { 1990178825Sdfr if (seteuid(pw->pw_uid) < 0) 1991178825Sdfr fatal("Failed to seteuid"); 199255682Smarkm goto pasv_error; 199355682Smarkm } 1994178825Sdfr if (seteuid(pw->pw_uid) < 0) 1995178825Sdfr fatal("Failed to seteuid"); 199655682Smarkm len = sizeof(pasv_addr_ss); 199755682Smarkm if (getsockname(pdata, pasv_addr, &len) < 0) 199855682Smarkm goto pasv_error; 199955682Smarkm if (listen(pdata, 1) < 0) 200055682Smarkm goto pasv_error; 200155682Smarkm sin = (struct sockaddr_in *)pasv_addr; 200255682Smarkm a = (char *) &sin->sin_addr; 200355682Smarkm p = (char *) &sin->sin_port; 200455682Smarkm 200555682Smarkm#define UC(b) (((int) b) & 0xff) 200655682Smarkm 200755682Smarkm reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 200855682Smarkm UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 200955682Smarkm return; 201055682Smarkm 201155682Smarkmpasv_error: 201255682Smarkm close(pdata); 201355682Smarkm pdata = -1; 201455682Smarkm perror_reply(425, "Can't open passive connection"); 201555682Smarkm return; 201655682Smarkm} 201755682Smarkm 201855682Smarkmvoid 201955682Smarkmepsv(char *proto) 202055682Smarkm{ 202172445Sassar socklen_t len; 202255682Smarkm 202355682Smarkm pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0); 202455682Smarkm if (pdata < 0) { 202555682Smarkm perror_reply(425, "Can't open passive connection"); 202655682Smarkm return; 202755682Smarkm } 202855682Smarkm pasv_addr->sa_family = ctrl_addr->sa_family; 202955682Smarkm socket_set_address_and_port (pasv_addr, 203055682Smarkm socket_get_address (ctrl_addr), 203155682Smarkm 0); 2032233294Sstas socket_set_portrange(pdata, restricted_data_ports, 2033233294Sstas pasv_addr->sa_family); 2034178825Sdfr if (seteuid(0) < 0) 2035178825Sdfr fatal("Failed to seteuid"); 203655682Smarkm if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) { 2037178825Sdfr if (seteuid(pw->pw_uid)) 2038178825Sdfr fatal("Failed to seteuid"); 203955682Smarkm goto pasv_error; 204055682Smarkm } 2041178825Sdfr if (seteuid(pw->pw_uid) < 0) 2042178825Sdfr fatal("Failed to seteuid"); 204355682Smarkm len = sizeof(pasv_addr_ss); 204455682Smarkm if (getsockname(pdata, pasv_addr, &len) < 0) 204555682Smarkm goto pasv_error; 204655682Smarkm if (listen(pdata, 1) < 0) 204755682Smarkm goto pasv_error; 204855682Smarkm 204955682Smarkm reply(229, "Entering Extended Passive Mode (|||%d|)", 205055682Smarkm ntohs(socket_get_port (pasv_addr))); 205155682Smarkm return; 205255682Smarkm 205355682Smarkmpasv_error: 205455682Smarkm close(pdata); 205555682Smarkm pdata = -1; 205655682Smarkm perror_reply(425, "Can't open passive connection"); 205755682Smarkm return; 205855682Smarkm} 205955682Smarkm 206055682Smarkmvoid 206155682Smarkmeprt(char *str) 206255682Smarkm{ 206355682Smarkm char *end; 206455682Smarkm char sep; 206555682Smarkm int af; 206655682Smarkm int ret; 206755682Smarkm int port; 206855682Smarkm 206955682Smarkm usedefault = 0; 207055682Smarkm if (pdata >= 0) { 207155682Smarkm close(pdata); 207255682Smarkm pdata = -1; 207355682Smarkm } 207455682Smarkm 207555682Smarkm sep = *str++; 207655682Smarkm if (sep == '\0') { 207755682Smarkm reply(500, "Bad syntax in EPRT"); 207855682Smarkm return; 207955682Smarkm } 208055682Smarkm af = strtol (str, &end, 0); 208155682Smarkm if (af == 0 || *end != sep) { 208255682Smarkm reply(500, "Bad syntax in EPRT"); 208355682Smarkm return; 208455682Smarkm } 208555682Smarkm str = end + 1; 208655682Smarkm switch (af) { 208755682Smarkm#ifdef HAVE_IPV6 208855682Smarkm case 2 : 208955682Smarkm data_dest->sa_family = AF_INET6; 209055682Smarkm break; 2091233294Sstas#endif 209255682Smarkm case 1 : 209355682Smarkm data_dest->sa_family = AF_INET; 209455682Smarkm break; 209555682Smarkm default : 209655682Smarkm reply(522, "Network protocol %d not supported, use (1" 209755682Smarkm#ifdef HAVE_IPV6 209855682Smarkm ",2" 209955682Smarkm#endif 210055682Smarkm ")", af); 210155682Smarkm return; 210255682Smarkm } 210355682Smarkm end = strchr (str, sep); 210455682Smarkm if (end == NULL) { 210555682Smarkm reply(500, "Bad syntax in EPRT"); 210655682Smarkm return; 210755682Smarkm } 210855682Smarkm *end = '\0'; 210955682Smarkm ret = inet_pton (data_dest->sa_family, str, 211055682Smarkm socket_get_address (data_dest)); 211155682Smarkm 211255682Smarkm if (ret != 1) { 211355682Smarkm reply(500, "Bad address syntax in EPRT"); 211455682Smarkm return; 211555682Smarkm } 211655682Smarkm str = end + 1; 211755682Smarkm port = strtol (str, &end, 0); 211855682Smarkm if (port == 0 || *end != sep) { 211955682Smarkm reply(500, "Bad port syntax in EPRT"); 212055682Smarkm return; 212155682Smarkm } 2122233294Sstas if (port < IPPORT_RESERVED) { 2123233294Sstas reply(500, "Bad port in invalid range in EPRT"); 2124233294Sstas return; 2125233294Sstas } 212655682Smarkm socket_set_port (data_dest, htons(port)); 2127233294Sstas 2128233294Sstas if (paranoid && 2129233294Sstas (data_dest->sa_family != his_addr->sa_family || 2130233294Sstas memcmp(socket_get_address(data_dest), socket_get_address(his_addr), socket_sockaddr_size(data_dest)) != 0)) 2131233294Sstas { 2132233294Sstas reply(500, "Bad address in EPRT"); 2133233294Sstas } 213455682Smarkm reply(200, "EPRT command successful."); 213555682Smarkm} 213655682Smarkm 213755682Smarkm/* 213855682Smarkm * Generate unique name for file with basename "local". 213955682Smarkm * The file named "local" is already known to exist. 214055682Smarkm * Generates failure reply on error. 214155682Smarkm */ 214255682Smarkmstatic char * 214355682Smarkmgunique(char *local) 214455682Smarkm{ 214555682Smarkm static char new[MaxPathLen]; 214655682Smarkm struct stat st; 214755682Smarkm int count; 214855682Smarkm char *cp; 214955682Smarkm 215055682Smarkm cp = strrchr(local, '/'); 215155682Smarkm if (cp) 215255682Smarkm *cp = '\0'; 215355682Smarkm if (stat(cp ? local : ".", &st) < 0) { 215455682Smarkm perror_reply(553, cp ? local : "."); 215555682Smarkm return NULL; 215655682Smarkm } 215755682Smarkm if (cp) 215855682Smarkm *cp = '/'; 215955682Smarkm for (count = 1; count < 100; count++) { 216055682Smarkm snprintf (new, sizeof(new), "%s.%d", local, count); 216155682Smarkm if (stat(new, &st) < 0) 216255682Smarkm return (new); 216355682Smarkm } 216455682Smarkm reply(452, "Unique file name cannot be created."); 216555682Smarkm return (NULL); 216655682Smarkm} 216755682Smarkm 216855682Smarkm/* 216955682Smarkm * Format and send reply containing system error number. 217055682Smarkm */ 217155682Smarkmvoid 217255682Smarkmperror_reply(int code, const char *string) 217355682Smarkm{ 217455682Smarkm reply(code, "%s: %s.", string, strerror(errno)); 217555682Smarkm} 217655682Smarkm 217755682Smarkmstatic char *onefile[] = { 217855682Smarkm "", 217955682Smarkm 0 218055682Smarkm}; 218155682Smarkm 218255682Smarkmvoid 218355682Smarkmlist_file(char *file) 218455682Smarkm{ 218555682Smarkm if(use_builtin_ls) { 218655682Smarkm FILE *dout; 218755682Smarkm dout = dataconn(file, -1, "w"); 218855682Smarkm if (dout == NULL) 218955682Smarkm return; 219055682Smarkm set_buffer_size(fileno(dout), 0); 2191102644Snectar if(builtin_ls(dout, file) == 0) 2192102644Snectar reply(226, "Transfer complete."); 2193102644Snectar else 2194102644Snectar reply(451, "Requested action aborted. Local error in processing."); 219555682Smarkm fclose(dout); 219655682Smarkm data = -1; 219755682Smarkm pdata = -1; 219855682Smarkm } else { 219955682Smarkm#ifdef HAVE_LS_A 220072445Sassar const char *cmd = "/bin/ls -lA %s"; 220155682Smarkm#else 220272445Sassar const char *cmd = "/bin/ls -la %s"; 220355682Smarkm#endif 220455682Smarkm retrieve(cmd, file); 220555682Smarkm } 220655682Smarkm} 220755682Smarkm 220855682Smarkmvoid 220955682Smarkmsend_file_list(char *whichf) 221055682Smarkm{ 2211142403Snectar struct stat st; 2212142403Snectar DIR *dirp = NULL; 2213142403Snectar struct dirent *dir; 2214142403Snectar FILE *dout = NULL; 2215142403Snectar char **dirlist, *dirname; 2216142403Snectar int simple = 0; 2217142403Snectar int freeglob = 0; 2218142403Snectar glob_t gl; 2219142403Snectar char buf[MaxPathLen]; 222055682Smarkm 2221142403Snectar if (strpbrk(whichf, "~{[*?") != NULL) { 2222142403Snectar int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE| 222390926Snectar#ifdef GLOB_MAXPATH 2224142403Snectar GLOB_MAXPATH 222590926Snectar#else 2226142403Snectar GLOB_LIMIT 222790926Snectar#endif 2228142403Snectar ; 222955682Smarkm 2230142403Snectar memset(&gl, 0, sizeof(gl)); 2231142403Snectar freeglob = 1; 2232142403Snectar if (glob(whichf, flags, 0, &gl)) { 2233142403Snectar reply(550, "not found"); 2234142403Snectar goto out; 2235142403Snectar } else if (gl.gl_pathc == 0) { 2236142403Snectar errno = ENOENT; 2237142403Snectar perror_reply(550, whichf); 2238142403Snectar goto out; 2239142403Snectar } 2240142403Snectar dirlist = gl.gl_pathv; 2241142403Snectar } else { 2242142403Snectar onefile[0] = whichf; 2243142403Snectar dirlist = onefile; 2244142403Snectar simple = 1; 224555682Smarkm } 224655682Smarkm 2247142403Snectar while ((dirname = *dirlist++)) { 224855682Smarkm 2249142403Snectar if (urgflag && handleoobcmd()) 2250142403Snectar goto out; 225155682Smarkm 2252142403Snectar if (stat(dirname, &st) < 0) { 2253142403Snectar /* 2254142403Snectar * If user typed "ls -l", etc, and the client 2255142403Snectar * used NLST, do what the user meant. 2256142403Snectar */ 2257142403Snectar if (dirname[0] == '-' && *dirlist == NULL && 2258142403Snectar transflag == 0) { 2259142403Snectar list_file(dirname); 2260142403Snectar goto out; 2261142403Snectar } 2262142403Snectar perror_reply(550, whichf); 2263142403Snectar goto out; 2264142403Snectar } 226555682Smarkm 2266142403Snectar if (S_ISREG(st.st_mode)) { 2267142403Snectar if (dout == NULL) { 2268142403Snectar dout = dataconn("file list", (off_t)-1, "w"); 2269142403Snectar if (dout == NULL) 2270142403Snectar goto out; 2271142403Snectar transflag = 1; 2272142403Snectar } 2273142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", dirname, 2274142403Snectar type == TYPE_A ? "\r" : ""); 2275142403Snectar sec_write(fileno(dout), buf, strlen(buf)); 2276142403Snectar byte_count += strlen(dirname) + 1; 2277142403Snectar continue; 2278142403Snectar } else if (!S_ISDIR(st.st_mode)) 2279142403Snectar continue; 228055682Smarkm 2281142403Snectar if ((dirp = opendir(dirname)) == NULL) 2282142403Snectar continue; 228355682Smarkm 2284142403Snectar while ((dir = readdir(dirp)) != NULL) { 2285142403Snectar char nbuf[MaxPathLen]; 228655682Smarkm 2287142403Snectar if (urgflag && handleoobcmd()) 2288142403Snectar goto out; 2289142403Snectar 2290142403Snectar if (!strcmp(dir->d_name, ".")) 2291142403Snectar continue; 2292142403Snectar if (!strcmp(dir->d_name, "..")) 2293142403Snectar continue; 2294142403Snectar 2295142403Snectar snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); 2296142403Snectar 2297142403Snectar /* 2298142403Snectar * We have to do a stat to insure it's 2299142403Snectar * not a directory or special file. 2300142403Snectar */ 2301142403Snectar if (simple || (stat(nbuf, &st) == 0 && 2302142403Snectar S_ISREG(st.st_mode))) { 2303142403Snectar if (dout == NULL) { 2304142403Snectar dout = dataconn("file list", (off_t)-1, "w"); 2305142403Snectar if (dout == NULL) 2306142403Snectar goto out; 2307142403Snectar transflag = 1; 2308142403Snectar } 2309142403Snectar if(strncmp(nbuf, "./", 2) == 0) 2310142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2, 2311142403Snectar type == TYPE_A ? "\r" : ""); 2312142403Snectar else 2313142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", nbuf, 2314142403Snectar type == TYPE_A ? "\r" : ""); 2315142403Snectar sec_write(fileno(dout), buf, strlen(buf)); 2316142403Snectar byte_count += strlen(nbuf) + 1; 2317142403Snectar } 231855682Smarkm } 2319142403Snectar closedir(dirp); 232055682Smarkm } 2321142403Snectar if (dout == NULL) 2322142403Snectar reply(550, "No files found."); 2323142403Snectar else if (ferror(dout) != 0) 2324142403Snectar perror_reply(550, "Data connection"); 2325142403Snectar else 2326142403Snectar reply(226, "Transfer complete."); 232755682Smarkm 2328142403Snectarout: 2329142403Snectar transflag = 0; 2330142403Snectar if (dout != NULL){ 2331142403Snectar sec_write(fileno(dout), buf, 0); /* XXX flush */ 2332233294Sstas 2333142403Snectar fclose(dout); 2334142403Snectar } 2335142403Snectar data = -1; 2336142403Snectar pdata = -1; 2337233294Sstas if (freeglob) 2338142403Snectar globfree(&gl); 233955682Smarkm} 234055682Smarkm 234155682Smarkm 234255682Smarkmint 234355682Smarkmfind(char *pattern) 234455682Smarkm{ 234555682Smarkm char line[1024]; 234655682Smarkm FILE *f; 234755682Smarkm 234855682Smarkm snprintf(line, sizeof(line), 234955682Smarkm "/bin/locate -d %s -- %s", 235055682Smarkm ftp_rooted("/etc/locatedb"), 235155682Smarkm pattern); 235255682Smarkm f = ftpd_popen(line, "r", 1, 1); 235355682Smarkm if(f == NULL){ 235455682Smarkm perror_reply(550, "/bin/locate"); 235555682Smarkm return 1; 235655682Smarkm } 235755682Smarkm lreply(200, "Output from find."); 235855682Smarkm while(fgets(line, sizeof(line), f)){ 235955682Smarkm if(line[strlen(line)-1] == '\n') 236055682Smarkm line[strlen(line)-1] = 0; 236155682Smarkm nreply("%s", line); 236255682Smarkm } 236355682Smarkm reply(200, "Done"); 236455682Smarkm ftpd_pclose(f); 236555682Smarkm return 0; 236655682Smarkm} 236755682Smarkm 2368