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 41178825SdfrRCSID("$Id: ftpd.c 21222 2007-06-20 10:11:14Z lha $"); 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]; 9455682Smarkm 9555682Smarkm#define AUTH_PLAIN (1 << 0) /* allow sending passwords */ 9655682Smarkm#define AUTH_OTP (1 << 1) /* passwords are one-time */ 9755682Smarkm#define AUTH_FTP (1 << 2) /* allow anonymous login */ 9855682Smarkm 9955682Smarkmstatic int auth_level = 0; /* Only allow kerberos login by default */ 10055682Smarkm 10155682Smarkm/* 10255682Smarkm * Timeout intervals for retrying connections 10355682Smarkm * to hosts that don't accept PORT cmds. This 10455682Smarkm * is a kludge, but given the problems with TCP... 10555682Smarkm */ 10655682Smarkm#define SWAITMAX 90 /* wait at most 90 seconds */ 10755682Smarkm#define SWAITINT 5 /* interval between retries */ 10855682Smarkm 10955682Smarkmint swaitmax = SWAITMAX; 11055682Smarkmint swaitint = SWAITINT; 11155682Smarkm 11255682Smarkm#ifdef HAVE_SETPROCTITLE 11355682Smarkmchar proctitle[BUFSIZ]; /* initial part of title */ 11455682Smarkm#endif /* HAVE_SETPROCTITLE */ 11555682Smarkm 11655682Smarkm#define LOGCMD(cmd, file) \ 11755682Smarkm if (logging > 1) \ 11855682Smarkm syslog(LOG_INFO,"%s %s%s", cmd, \ 11955682Smarkm *(file) == '/' ? "" : curdir(), file); 12055682Smarkm#define LOGCMD2(cmd, file1, file2) \ 12155682Smarkm if (logging > 1) \ 12255682Smarkm syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 12355682Smarkm *(file1) == '/' ? "" : curdir(), file1, \ 12455682Smarkm *(file2) == '/' ? "" : curdir(), file2); 12555682Smarkm#define LOGBYTES(cmd, file, cnt) \ 12655682Smarkm if (logging > 1) { \ 12755682Smarkm if (cnt == (off_t)-1) \ 12855682Smarkm syslog(LOG_INFO,"%s %s%s", cmd, \ 12955682Smarkm *(file) == '/' ? "" : curdir(), file); \ 13055682Smarkm else \ 13155682Smarkm syslog(LOG_INFO, "%s %s%s = %ld bytes", \ 13255682Smarkm cmd, (*(file) == '/') ? "" : curdir(), file, (long)cnt); \ 13355682Smarkm } 13455682Smarkm 13555682Smarkmstatic void ack (char *); 13655682Smarkmstatic void myoob (int); 137142403Snectarstatic int handleoobcmd(void); 13855682Smarkmstatic int checkuser (char *, char *); 13955682Smarkmstatic int checkaccess (char *); 14055682Smarkmstatic FILE *dataconn (const char *, off_t, const char *); 141178825Sdfrstatic void dolog (struct sockaddr *, int); 14255682Smarkmstatic void end_login (void); 143178825Sdfrstatic FILE *getdatasock (const char *, int); 14455682Smarkmstatic char *gunique (char *); 14555682Smarkmstatic RETSIGTYPE lostconn (int); 14655682Smarkmstatic int receive_data (FILE *, FILE *); 14755682Smarkmstatic void send_data (FILE *, FILE *); 14855682Smarkmstatic struct passwd * sgetpwnam (char *); 14955682Smarkm 15055682Smarkmstatic char * 15155682Smarkmcurdir(void) 15255682Smarkm{ 15355682Smarkm static char path[MaxPathLen+1]; /* path + '/' + '\0' */ 15455682Smarkm 15555682Smarkm if (getcwd(path, sizeof(path)-1) == NULL) 15655682Smarkm return (""); 15755682Smarkm if (path[1] != '\0') /* special case for root dir. */ 15855682Smarkm strlcat(path, "/", sizeof(path)); 15955682Smarkm /* For guest account, skip / since it's chrooted */ 16055682Smarkm return (guest ? path+1 : path); 16155682Smarkm} 16255682Smarkm 16355682Smarkm#ifndef LINE_MAX 16455682Smarkm#define LINE_MAX 1024 16555682Smarkm#endif 16655682Smarkm 16755682Smarkmstatic int 16855682Smarkmparse_auth_level(char *str) 16955682Smarkm{ 17055682Smarkm char *p; 17155682Smarkm int ret = 0; 17255682Smarkm char *foo = NULL; 17355682Smarkm 17455682Smarkm for(p = strtok_r(str, ",", &foo); 17555682Smarkm p; 17655682Smarkm p = strtok_r(NULL, ",", &foo)) { 17755682Smarkm if(strcmp(p, "user") == 0) 17855682Smarkm ; 17955682Smarkm#ifdef OTP 18055682Smarkm else if(strcmp(p, "otp") == 0) 18155682Smarkm ret |= AUTH_PLAIN|AUTH_OTP; 18255682Smarkm#endif 18355682Smarkm else if(strcmp(p, "ftp") == 0 || 18455682Smarkm strcmp(p, "safe") == 0) 18555682Smarkm ret |= AUTH_FTP; 18655682Smarkm else if(strcmp(p, "plain") == 0) 18755682Smarkm ret |= AUTH_PLAIN; 18855682Smarkm else if(strcmp(p, "none") == 0) 18955682Smarkm ret |= AUTH_PLAIN|AUTH_FTP; 19055682Smarkm else 19155682Smarkm warnx("bad value for -a: `%s'", p); 19255682Smarkm } 19355682Smarkm return ret; 19455682Smarkm} 19555682Smarkm 19655682Smarkm/* 19755682Smarkm * Print usage and die. 19855682Smarkm */ 19955682Smarkm 20055682Smarkmstatic int interactive_flag; 20155682Smarkmstatic char *guest_umask_string; 20255682Smarkmstatic char *port_string; 20355682Smarkmstatic char *umask_string; 20455682Smarkmstatic char *auth_string; 20555682Smarkm 20655682Smarkmint use_builtin_ls = -1; 20755682Smarkm 20855682Smarkmstatic int help_flag; 20955682Smarkmstatic int version_flag; 21055682Smarkm 21172445Sassarstatic const char *good_chars = "+-=_,."; 21272445Sassar 21355682Smarkmstruct getargs args[] = { 21455682Smarkm { NULL, 'a', arg_string, &auth_string, "required authentication" }, 21555682Smarkm { NULL, 'i', arg_flag, &interactive_flag, "don't assume stdin is a socket" }, 21655682Smarkm { NULL, 'p', arg_string, &port_string, "what port to listen to" }, 21755682Smarkm { NULL, 'g', arg_string, &guest_umask_string, "umask for guest logins" }, 21855682Smarkm { NULL, 'l', arg_counter, &logging, "log more stuff", "" }, 21955682Smarkm { NULL, 't', arg_integer, &ftpd_timeout, "initial timeout" }, 22055682Smarkm { NULL, 'T', arg_integer, &maxtimeout, "max timeout" }, 22155682Smarkm { NULL, 'u', arg_string, &umask_string, "umask for user logins" }, 22290926Snectar { NULL, 'U', arg_negative_flag, &restricted_data_ports, "don't use high data ports" }, 22372445Sassar { NULL, 'd', arg_flag, &debug, "enable debugging" }, 22472445Sassar { NULL, 'v', arg_flag, &debug, "enable debugging" }, 22555682Smarkm { "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" }, 22672445Sassar { "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" }, 227142403Snectar { "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" }, 228127808Snectar#ifdef KRB5 229127808Snectar { "gss-bindings", 0, arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL}, 230127808Snectar#endif 23155682Smarkm { "version", 0, arg_flag, &version_flag }, 23255682Smarkm { "help", 'h', arg_flag, &help_flag } 23355682Smarkm}; 23455682Smarkm 23555682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]); 23655682Smarkm 23755682Smarkmstatic void 23855682Smarkmusage (int code) 23955682Smarkm{ 24055682Smarkm arg_printusage(args, num_args, NULL, ""); 24155682Smarkm exit (code); 24255682Smarkm} 24355682Smarkm 24455682Smarkm/* output contents of a file */ 24555682Smarkmstatic int 24655682Smarkmshow_file(const char *file, int code) 24755682Smarkm{ 24855682Smarkm FILE *f; 24955682Smarkm char buf[128]; 25055682Smarkm 25155682Smarkm f = fopen(file, "r"); 25255682Smarkm if(f == NULL) 25355682Smarkm return -1; 25455682Smarkm while(fgets(buf, sizeof(buf), f)){ 25555682Smarkm buf[strcspn(buf, "\r\n")] = '\0'; 25655682Smarkm lreply(code, "%s", buf); 25755682Smarkm } 25855682Smarkm fclose(f); 25955682Smarkm return 0; 26055682Smarkm} 26155682Smarkm 26255682Smarkmint 26355682Smarkmmain(int argc, char **argv) 26455682Smarkm{ 26572445Sassar socklen_t his_addr_len, ctrl_addr_len; 26672445Sassar int on = 1; 26755682Smarkm int port; 26855682Smarkm struct servent *sp; 26955682Smarkm 27055682Smarkm int optind = 0; 27155682Smarkm 27278527Sassar setprogname (argv[0]); 27355682Smarkm 27455682Smarkm /* detach from any tickets and tokens */ 27555682Smarkm { 27672445Sassar#ifdef KRB4 27755682Smarkm char tkfile[1024]; 27855682Smarkm snprintf(tkfile, sizeof(tkfile), 27955682Smarkm "/tmp/ftp_%u", (unsigned)getpid()); 28055682Smarkm krb_set_tkt_string(tkfile); 28172445Sassar#endif 282127808Snectar } 28372445Sassar 28455682Smarkm if(getarg(args, num_args, argc, argv, &optind)) 28555682Smarkm usage(1); 28655682Smarkm 28755682Smarkm if(help_flag) 28855682Smarkm usage(0); 28955682Smarkm 29055682Smarkm if(version_flag) { 29155682Smarkm print_version(NULL); 29255682Smarkm exit(0); 29355682Smarkm } 29455682Smarkm 29555682Smarkm if(auth_string) 29655682Smarkm auth_level = parse_auth_level(auth_string); 29755682Smarkm { 29855682Smarkm char *p; 29955682Smarkm long val = 0; 30055682Smarkm 30155682Smarkm if(guest_umask_string) { 30255682Smarkm val = strtol(guest_umask_string, &p, 8); 30355682Smarkm if (*p != '\0' || val < 0) 30455682Smarkm warnx("bad value for -g"); 30555682Smarkm else 30655682Smarkm guest_umask = val; 30755682Smarkm } 30855682Smarkm if(umask_string) { 30955682Smarkm val = strtol(umask_string, &p, 8); 31055682Smarkm if (*p != '\0' || val < 0) 31155682Smarkm warnx("bad value for -u"); 31255682Smarkm else 31355682Smarkm defumask = val; 31455682Smarkm } 31555682Smarkm } 316102644Snectar sp = getservbyname("ftp", "tcp"); 317102644Snectar if(sp) 318102644Snectar port = sp->s_port; 319102644Snectar else 320102644Snectar port = htons(21); 32155682Smarkm if(port_string) { 32255682Smarkm sp = getservbyname(port_string, "tcp"); 32355682Smarkm if(sp) 32455682Smarkm port = sp->s_port; 32555682Smarkm else 326120945Snectar if(isdigit((unsigned char)port_string[0])) 32755682Smarkm port = htons(atoi(port_string)); 32855682Smarkm else 32955682Smarkm warnx("bad value for -p"); 33055682Smarkm } 33155682Smarkm 33255682Smarkm if (maxtimeout < ftpd_timeout) 33355682Smarkm maxtimeout = ftpd_timeout; 33455682Smarkm 33555682Smarkm#if 0 33655682Smarkm if (ftpd_timeout > maxtimeout) 33755682Smarkm ftpd_timeout = maxtimeout; 33855682Smarkm#endif 33955682Smarkm 34055682Smarkm if(interactive_flag) 34155682Smarkm mini_inetd (port); 34255682Smarkm 34355682Smarkm /* 34455682Smarkm * LOG_NDELAY sets up the logging connection immediately, 34555682Smarkm * necessary for anonymous ftp's that chroot and can't do it later. 34655682Smarkm */ 34755682Smarkm openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 34855682Smarkm his_addr_len = sizeof(his_addr_ss); 34955682Smarkm if (getpeername(STDIN_FILENO, his_addr, &his_addr_len) < 0) { 35055682Smarkm syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 35155682Smarkm exit(1); 35255682Smarkm } 35355682Smarkm ctrl_addr_len = sizeof(ctrl_addr_ss); 35455682Smarkm if (getsockname(STDIN_FILENO, ctrl_addr, &ctrl_addr_len) < 0) { 35555682Smarkm syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 35655682Smarkm exit(1); 35755682Smarkm } 35855682Smarkm#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) 35972445Sassar { 36072445Sassar int tos = IPTOS_LOWDELAY; 36172445Sassar 36272445Sassar if (setsockopt(STDIN_FILENO, IPPROTO_IP, IP_TOS, 36372445Sassar (void *)&tos, sizeof(int)) < 0) 36472445Sassar syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 36572445Sassar } 36655682Smarkm#endif 36755682Smarkm data_source->sa_family = ctrl_addr->sa_family; 36855682Smarkm socket_set_port (data_source, 36955682Smarkm htons(ntohs(socket_get_port(ctrl_addr)) - 1)); 37055682Smarkm 37155682Smarkm /* set this here so it can be put in wtmp */ 37255682Smarkm snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid()); 37355682Smarkm 37455682Smarkm 37555682Smarkm /* freopen(_PATH_DEVNULL, "w", stderr); */ 37655682Smarkm signal(SIGPIPE, lostconn); 37755682Smarkm signal(SIGCHLD, SIG_IGN); 37855682Smarkm#ifdef SIGURG 37955682Smarkm if (signal(SIGURG, myoob) == SIG_ERR) 38055682Smarkm syslog(LOG_ERR, "signal: %m"); 38155682Smarkm#endif 38255682Smarkm 38355682Smarkm /* Try to handle urgent data inline */ 38455682Smarkm#if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT) 38555682Smarkm if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (void *)&on, 38655682Smarkm sizeof(on)) < 0) 38755682Smarkm syslog(LOG_ERR, "setsockopt: %m"); 38855682Smarkm#endif 38955682Smarkm 39055682Smarkm#ifdef F_SETOWN 39155682Smarkm if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 39255682Smarkm syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 39355682Smarkm#endif 39455682Smarkm dolog(his_addr, his_addr_len); 39555682Smarkm /* 39655682Smarkm * Set up default state 39755682Smarkm */ 39855682Smarkm data = -1; 39955682Smarkm type = TYPE_A; 40055682Smarkm form = FORM_N; 40155682Smarkm stru = STRU_F; 40255682Smarkm mode = MODE_S; 40355682Smarkm tmpline[0] = '\0'; 40455682Smarkm 40555682Smarkm /* If logins are disabled, print out the message. */ 40655682Smarkm if(show_file(_PATH_NOLOGIN, 530) == 0) { 40755682Smarkm reply(530, "System not available."); 40855682Smarkm exit(0); 40955682Smarkm } 41055682Smarkm show_file(_PATH_FTPWELCOME, 220); 41155682Smarkm /* reply(220,) must follow */ 41255682Smarkm gethostname(hostname, sizeof(hostname)); 41355682Smarkm 41455682Smarkm reply(220, "%s FTP server (%s" 41555682Smarkm#ifdef KRB5 41655682Smarkm "+%s" 41755682Smarkm#endif 41855682Smarkm#ifdef KRB4 41955682Smarkm "+%s" 42055682Smarkm#endif 42155682Smarkm ") ready.", hostname, version 42255682Smarkm#ifdef KRB5 42355682Smarkm ,heimdal_version 42455682Smarkm#endif 42555682Smarkm#ifdef KRB4 42655682Smarkm ,krb4_version 42755682Smarkm#endif 42855682Smarkm ); 42955682Smarkm 43055682Smarkm for (;;) 43155682Smarkm yyparse(); 43255682Smarkm /* NOTREACHED */ 43355682Smarkm} 43455682Smarkm 43555682Smarkmstatic RETSIGTYPE 43655682Smarkmlostconn(int signo) 43755682Smarkm{ 43855682Smarkm 43955682Smarkm if (debug) 44055682Smarkm syslog(LOG_DEBUG, "lost connection"); 44155682Smarkm dologout(-1); 44255682Smarkm} 44355682Smarkm 44455682Smarkm/* 44555682Smarkm * Helper function for sgetpwnam(). 44655682Smarkm */ 44755682Smarkmstatic char * 44855682Smarkmsgetsave(char *s) 44955682Smarkm{ 45055682Smarkm char *new = strdup(s); 45155682Smarkm 45255682Smarkm if (new == NULL) { 45355682Smarkm perror_reply(421, "Local resource failure: malloc"); 45455682Smarkm dologout(1); 45555682Smarkm /* NOTREACHED */ 45655682Smarkm } 45755682Smarkm return new; 45855682Smarkm} 45955682Smarkm 46055682Smarkm/* 46155682Smarkm * Save the result of a getpwnam. Used for USER command, since 46255682Smarkm * the data returned must not be clobbered by any other command 46355682Smarkm * (e.g., globbing). 46455682Smarkm */ 46555682Smarkmstatic struct passwd * 46655682Smarkmsgetpwnam(char *name) 46755682Smarkm{ 46855682Smarkm static struct passwd save; 46955682Smarkm struct passwd *p; 47055682Smarkm 47155682Smarkm if ((p = k_getpwnam(name)) == NULL) 47255682Smarkm return (p); 47355682Smarkm if (save.pw_name) { 47455682Smarkm free(save.pw_name); 47555682Smarkm free(save.pw_passwd); 47655682Smarkm free(save.pw_gecos); 47755682Smarkm free(save.pw_dir); 47855682Smarkm free(save.pw_shell); 47955682Smarkm } 48055682Smarkm save = *p; 48155682Smarkm save.pw_name = sgetsave(p->pw_name); 48255682Smarkm save.pw_passwd = sgetsave(p->pw_passwd); 48355682Smarkm save.pw_gecos = sgetsave(p->pw_gecos); 48455682Smarkm save.pw_dir = sgetsave(p->pw_dir); 48555682Smarkm save.pw_shell = sgetsave(p->pw_shell); 48655682Smarkm return (&save); 48755682Smarkm} 48855682Smarkm 48955682Smarkmstatic int login_attempts; /* number of failed login attempts */ 49055682Smarkmstatic int askpasswd; /* had user command, ask for passwd */ 49155682Smarkmstatic char curname[10]; /* current USER name */ 49255682Smarkm#ifdef OTP 49355682SmarkmOtpContext otp_ctx; 49455682Smarkm#endif 49555682Smarkm 49655682Smarkm/* 49755682Smarkm * USER command. 49855682Smarkm * Sets global passwd pointer pw if named account exists and is acceptable; 49955682Smarkm * sets askpasswd if a PASS command is expected. If logged in previously, 50055682Smarkm * need to reset state. If name is "ftp" or "anonymous", the name is not in 50155682Smarkm * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 50255682Smarkm * If account doesn't exist, ask for passwd anyway. Otherwise, check user 50355682Smarkm * requesting login privileges. Disallow anyone who does not have a standard 50455682Smarkm * shell as returned by getusershell(). Disallow anyone mentioned in the file 50555682Smarkm * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 50655682Smarkm */ 50755682Smarkmvoid 50855682Smarkmuser(char *name) 50955682Smarkm{ 51055682Smarkm char *cp, *shell; 51155682Smarkm 51255682Smarkm if(auth_level == 0 && !sec_complete){ 51355682Smarkm reply(530, "No login allowed without authorization."); 51455682Smarkm return; 51555682Smarkm } 51655682Smarkm 51755682Smarkm if (logged_in) { 51855682Smarkm if (guest) { 51955682Smarkm reply(530, "Can't change user from guest login."); 52055682Smarkm return; 52155682Smarkm } else if (dochroot) { 52255682Smarkm reply(530, "Can't change user from chroot user."); 52355682Smarkm return; 52455682Smarkm } 52555682Smarkm end_login(); 52655682Smarkm } 52755682Smarkm 52855682Smarkm guest = 0; 52955682Smarkm if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 53055682Smarkm if ((auth_level & AUTH_FTP) == 0 || 53155682Smarkm checkaccess("ftp") || 53255682Smarkm checkaccess("anonymous")) 53355682Smarkm reply(530, "User %s access denied.", name); 53455682Smarkm else if ((pw = sgetpwnam("ftp")) != NULL) { 53555682Smarkm guest = 1; 53655682Smarkm defumask = guest_umask; /* paranoia for incoming */ 53755682Smarkm askpasswd = 1; 53855682Smarkm reply(331, "Guest login ok, type your name as password."); 53955682Smarkm } else 54055682Smarkm reply(530, "User %s unknown.", name); 54155682Smarkm if (!askpasswd && logging) { 54255682Smarkm char data_addr[256]; 54355682Smarkm 54455682Smarkm if (inet_ntop (his_addr->sa_family, 54555682Smarkm socket_get_address(his_addr), 54655682Smarkm data_addr, sizeof(data_addr)) == NULL) 54755682Smarkm strlcpy (data_addr, "unknown address", 54855682Smarkm sizeof(data_addr)); 54955682Smarkm 55055682Smarkm syslog(LOG_NOTICE, 55155682Smarkm "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)", 55255682Smarkm remotehost, data_addr); 55355682Smarkm } 55455682Smarkm return; 55555682Smarkm } 55655682Smarkm if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){ 55755682Smarkm reply(530, "Only authorized and anonymous login allowed."); 55855682Smarkm return; 55955682Smarkm } 56055682Smarkm if ((pw = sgetpwnam(name))) { 56155682Smarkm if ((shell = pw->pw_shell) == NULL || *shell == 0) 56255682Smarkm shell = _PATH_BSHELL; 56355682Smarkm while ((cp = getusershell()) != NULL) 56455682Smarkm if (strcmp(cp, shell) == 0) 56555682Smarkm break; 56655682Smarkm endusershell(); 56755682Smarkm 56855682Smarkm if (cp == NULL || checkaccess(name)) { 56955682Smarkm reply(530, "User %s access denied.", name); 57055682Smarkm if (logging) { 57155682Smarkm char data_addr[256]; 57255682Smarkm 57355682Smarkm if (inet_ntop (his_addr->sa_family, 57455682Smarkm socket_get_address(his_addr), 57555682Smarkm data_addr, 57655682Smarkm sizeof(data_addr)) == NULL) 57755682Smarkm strlcpy (data_addr, 57855682Smarkm "unknown address", 57955682Smarkm sizeof(data_addr)); 58055682Smarkm 58155682Smarkm syslog(LOG_NOTICE, 58255682Smarkm "FTP LOGIN REFUSED FROM %s(%s), %s", 58355682Smarkm remotehost, 58455682Smarkm data_addr, 58555682Smarkm name); 58655682Smarkm } 58755682Smarkm pw = (struct passwd *) NULL; 58855682Smarkm return; 58955682Smarkm } 59055682Smarkm } 59155682Smarkm if (logging) 59255682Smarkm strlcpy(curname, name, sizeof(curname)); 59355682Smarkm if(sec_complete) { 594178825Sdfr if(sec_userok(name) == 0) { 59555682Smarkm do_login(232, name); 596178825Sdfr sec_session(name); 597178825Sdfr } else 59855682Smarkm reply(530, "User %s access denied.", name); 59955682Smarkm } else { 600178825Sdfr#ifdef OTP 60155682Smarkm char ss[256]; 60255682Smarkm 60355682Smarkm if (otp_challenge(&otp_ctx, name, ss, sizeof(ss)) == 0) { 60455682Smarkm reply(331, "Password %s for %s required.", 60555682Smarkm ss, name); 60655682Smarkm askpasswd = 1; 60755682Smarkm } else 60855682Smarkm#endif 60955682Smarkm if ((auth_level & AUTH_OTP) == 0) { 61055682Smarkm reply(331, "Password required for %s.", name); 61155682Smarkm askpasswd = 1; 61255682Smarkm } else { 613178825Sdfr#ifdef OTP 61455682Smarkm char *s; 615178825Sdfr 61655682Smarkm if ((s = otp_error (&otp_ctx)) != NULL) 61755682Smarkm lreply(530, "OTP: %s", s); 61855682Smarkm#endif 61955682Smarkm reply(530, 62055682Smarkm "Only authorized, anonymous" 62155682Smarkm#ifdef OTP 62255682Smarkm " and OTP " 62355682Smarkm#endif 62455682Smarkm "login allowed."); 62555682Smarkm } 62655682Smarkm 62755682Smarkm } 62855682Smarkm /* 62955682Smarkm * Delay before reading passwd after first failed 63055682Smarkm * attempt to slow down passwd-guessing programs. 63155682Smarkm */ 63255682Smarkm if (login_attempts) 63355682Smarkm sleep(login_attempts); 63455682Smarkm} 63555682Smarkm 63655682Smarkm/* 63755682Smarkm * Check if a user is in the file "fname" 63855682Smarkm */ 63955682Smarkmstatic int 64055682Smarkmcheckuser(char *fname, char *name) 64155682Smarkm{ 64255682Smarkm FILE *fd; 64355682Smarkm int found = 0; 64455682Smarkm char *p, line[BUFSIZ]; 64555682Smarkm 64655682Smarkm if ((fd = fopen(fname, "r")) != NULL) { 64755682Smarkm while (fgets(line, sizeof(line), fd) != NULL) 64855682Smarkm if ((p = strchr(line, '\n')) != NULL) { 64955682Smarkm *p = '\0'; 65055682Smarkm if (line[0] == '#') 65155682Smarkm continue; 65255682Smarkm if (strcmp(line, name) == 0) { 65355682Smarkm found = 1; 65455682Smarkm break; 65555682Smarkm } 65655682Smarkm } 65755682Smarkm fclose(fd); 65855682Smarkm } 65955682Smarkm return (found); 66055682Smarkm} 66155682Smarkm 66255682Smarkm 66355682Smarkm/* 66455682Smarkm * Determine whether a user has access, based on information in 66555682Smarkm * _PATH_FTPUSERS. The users are listed one per line, with `allow' 66655682Smarkm * or `deny' after the username. If anything other than `allow', or 66755682Smarkm * just nothing, is given after the username, `deny' is assumed. 66855682Smarkm * 66955682Smarkm * If the user is not found in the file, but the pseudo-user `*' is, 67055682Smarkm * the permission is taken from that line. 67155682Smarkm * 67255682Smarkm * This preserves the old semantics where if a user was listed in the 67355682Smarkm * file he was denied, otherwise he was allowed. 67455682Smarkm * 67555682Smarkm * Return 1 if the user is denied, or 0 if he is allowed. */ 67655682Smarkm 67755682Smarkmstatic int 67855682Smarkmmatch(const char *pattern, const char *string) 67955682Smarkm{ 68055682Smarkm return fnmatch(pattern, string, FNM_NOESCAPE); 68155682Smarkm} 68255682Smarkm 68355682Smarkmstatic int 68455682Smarkmcheckaccess(char *name) 68555682Smarkm{ 68655682Smarkm#define ALLOWED 0 68755682Smarkm#define NOT_ALLOWED 1 68855682Smarkm FILE *fd; 68955682Smarkm int allowed = ALLOWED; 69055682Smarkm char *user, *perm, line[BUFSIZ]; 69155682Smarkm char *foo; 69255682Smarkm 69355682Smarkm fd = fopen(_PATH_FTPUSERS, "r"); 69455682Smarkm 69555682Smarkm if(fd == NULL) 69655682Smarkm return allowed; 69755682Smarkm 69855682Smarkm while (fgets(line, sizeof(line), fd) != NULL) { 69955682Smarkm foo = NULL; 70055682Smarkm user = strtok_r(line, " \t\n", &foo); 70155682Smarkm if (user == NULL || user[0] == '#') 70255682Smarkm continue; 70355682Smarkm perm = strtok_r(NULL, " \t\n", &foo); 70455682Smarkm if (match(user, name) == 0){ 70555682Smarkm if(perm && strcmp(perm, "allow") == 0) 70655682Smarkm allowed = ALLOWED; 70755682Smarkm else 70855682Smarkm allowed = NOT_ALLOWED; 70955682Smarkm break; 71055682Smarkm } 71155682Smarkm } 71255682Smarkm fclose(fd); 71355682Smarkm return allowed; 71455682Smarkm} 71555682Smarkm#undef ALLOWED 71655682Smarkm#undef NOT_ALLOWED 71755682Smarkm 71855682Smarkm 71955682Smarkmint do_login(int code, char *passwd) 72055682Smarkm{ 72155682Smarkm login_attempts = 0; /* this time successful */ 72255682Smarkm if (setegid((gid_t)pw->pw_gid) < 0) { 72355682Smarkm reply(550, "Can't set gid."); 72455682Smarkm return -1; 72555682Smarkm } 72655682Smarkm initgroups(pw->pw_name, pw->pw_gid); 727178825Sdfr#if defined(KRB4) || defined(KRB5) 728178825Sdfr if(k_hasafs()) 729178825Sdfr k_setpag(); 730178825Sdfr#endif 73155682Smarkm 73255682Smarkm /* open wtmp before chroot */ 73355682Smarkm ftpd_logwtmp(ttyline, pw->pw_name, remotehost); 73455682Smarkm logged_in = 1; 73555682Smarkm 73655682Smarkm dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); 73755682Smarkm if (guest) { 73855682Smarkm /* 73955682Smarkm * We MUST do a chdir() after the chroot. Otherwise 74055682Smarkm * the old current directory will be accessible as "." 74155682Smarkm * outside the new root! 74255682Smarkm */ 74355682Smarkm if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 74455682Smarkm reply(550, "Can't set guest privileges."); 74555682Smarkm return -1; 74655682Smarkm } 74755682Smarkm } else if (dochroot) { 74855682Smarkm if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 74955682Smarkm reply(550, "Can't change root."); 75055682Smarkm return -1; 75155682Smarkm } 75255682Smarkm } else if (chdir(pw->pw_dir) < 0) { 75355682Smarkm if (chdir("/") < 0) { 75455682Smarkm reply(530, "User %s: can't change directory to %s.", 75555682Smarkm pw->pw_name, pw->pw_dir); 75655682Smarkm return -1; 75755682Smarkm } else 75855682Smarkm lreply(code, "No directory! Logging in with home=/"); 75955682Smarkm } 76055682Smarkm if (seteuid((uid_t)pw->pw_uid) < 0) { 76155682Smarkm reply(550, "Can't set uid."); 76255682Smarkm return -1; 76355682Smarkm } 76455682Smarkm 76555682Smarkm if(use_builtin_ls == -1) { 76655682Smarkm struct stat st; 76755682Smarkm /* if /bin/ls exist and is a regular file, use it, otherwise 76855682Smarkm use built-in ls */ 76955682Smarkm if(stat("/bin/ls", &st) == 0 && 77055682Smarkm S_ISREG(st.st_mode)) 77155682Smarkm use_builtin_ls = 0; 77255682Smarkm else 77355682Smarkm use_builtin_ls = 1; 77455682Smarkm } 77555682Smarkm 77655682Smarkm /* 77755682Smarkm * Display a login message, if it exists. 77855682Smarkm * N.B. reply(code,) must follow the message. 77955682Smarkm */ 78055682Smarkm show_file(_PATH_FTPLOGINMESG, code); 78155682Smarkm if(show_file(_PATH_ISSUE_NET, code) != 0) 78255682Smarkm show_file(_PATH_ISSUE, code); 78355682Smarkm if (guest) { 78455682Smarkm reply(code, "Guest login ok, access restrictions apply."); 78555682Smarkm#ifdef HAVE_SETPROCTITLE 78655682Smarkm snprintf (proctitle, sizeof(proctitle), 78755682Smarkm "%s: anonymous/%s", 78855682Smarkm remotehost, 78955682Smarkm passwd); 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, "ANONYMOUS FTP LOGIN FROM %s(%s), %s", 80255682Smarkm remotehost, 80355682Smarkm data_addr, 80455682Smarkm passwd); 80555682Smarkm } 80655682Smarkm } else { 80755682Smarkm reply(code, "User %s logged in.", pw->pw_name); 80855682Smarkm#ifdef HAVE_SETPROCTITLE 80955682Smarkm snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); 81064593Skris setproctitle("%s", proctitle); 81155682Smarkm#endif /* HAVE_SETPROCTITLE */ 81255682Smarkm if (logging) { 81355682Smarkm char data_addr[256]; 81455682Smarkm 81555682Smarkm if (inet_ntop (his_addr->sa_family, 81655682Smarkm socket_get_address(his_addr), 81755682Smarkm data_addr, sizeof(data_addr)) == NULL) 81855682Smarkm strlcpy (data_addr, "unknown address", 81955682Smarkm sizeof(data_addr)); 82055682Smarkm 82155682Smarkm syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s", 82255682Smarkm remotehost, 82355682Smarkm data_addr, 82455682Smarkm pw->pw_name); 82555682Smarkm } 82655682Smarkm } 82755682Smarkm umask(defumask); 82855682Smarkm return 0; 82955682Smarkm} 83055682Smarkm 83155682Smarkm/* 83255682Smarkm * Terminate login as previous user, if any, resetting state; 83355682Smarkm * used when USER command is given or login fails. 83455682Smarkm */ 83555682Smarkmstatic void 83655682Smarkmend_login(void) 83755682Smarkm{ 83855682Smarkm 839178825Sdfr if (seteuid((uid_t)0) < 0) 840178825Sdfr fatal("Failed to seteuid"); 84155682Smarkm if (logged_in) 84255682Smarkm ftpd_logwtmp(ttyline, "", ""); 84355682Smarkm pw = NULL; 84455682Smarkm logged_in = 0; 84555682Smarkm guest = 0; 84655682Smarkm dochroot = 0; 84755682Smarkm} 84855682Smarkm 84972445Sassar#ifdef KRB5 85072445Sassarstatic int 85172445Sassarkrb5_verify(struct passwd *pwd, char *passwd) 85272445Sassar{ 85372445Sassar krb5_context context; 85472445Sassar krb5_ccache id; 85572445Sassar krb5_principal princ; 85672445Sassar krb5_error_code ret; 85772445Sassar 85872445Sassar ret = krb5_init_context(&context); 85972445Sassar if(ret) 86072445Sassar return ret; 86172445Sassar 86272445Sassar ret = krb5_parse_name(context, pwd->pw_name, &princ); 86372445Sassar if(ret){ 86472445Sassar krb5_free_context(context); 86572445Sassar return ret; 86672445Sassar } 86772445Sassar ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id); 86872445Sassar if(ret){ 86972445Sassar krb5_free_principal(context, princ); 87072445Sassar krb5_free_context(context); 87172445Sassar return ret; 87272445Sassar } 87372445Sassar ret = krb5_verify_user(context, 87472445Sassar princ, 87572445Sassar id, 87672445Sassar passwd, 87772445Sassar 1, 87872445Sassar NULL); 87972445Sassar krb5_free_principal(context, princ); 88072445Sassar if (k_hasafs()) { 88172445Sassar krb5_afslog_uid_home(context, id,NULL, NULL,pwd->pw_uid, pwd->pw_dir); 88272445Sassar } 88372445Sassar krb5_cc_destroy(context, id); 88472445Sassar krb5_free_context (context); 88572445Sassar if(ret) 88672445Sassar return ret; 88772445Sassar return 0; 88872445Sassar} 88972445Sassar#endif /* KRB5 */ 89072445Sassar 89155682Smarkmvoid 89255682Smarkmpass(char *passwd) 89355682Smarkm{ 89455682Smarkm int rval; 89555682Smarkm 89655682Smarkm /* some clients insists on sending a password */ 89755682Smarkm if (logged_in && askpasswd == 0){ 89872445Sassar reply(230, "Password not necessary"); 89972445Sassar return; 90055682Smarkm } 90155682Smarkm 90255682Smarkm if (logged_in || askpasswd == 0) { 90355682Smarkm reply(503, "Login with USER first."); 90455682Smarkm return; 90555682Smarkm } 90655682Smarkm askpasswd = 0; 90755682Smarkm rval = 1; 90855682Smarkm if (!guest) { /* "ftp" is only account allowed no password */ 90955682Smarkm if (pw == NULL) 91055682Smarkm rval = 1; /* failure below */ 91155682Smarkm#ifdef OTP 91255682Smarkm else if (otp_verify_user (&otp_ctx, passwd) == 0) { 91355682Smarkm rval = 0; 91455682Smarkm } 91555682Smarkm#endif 91655682Smarkm else if((auth_level & AUTH_OTP) == 0) { 91772445Sassar#ifdef KRB5 91872445Sassar rval = krb5_verify(pw, passwd); 91972445Sassar#endif 92055682Smarkm#ifdef KRB4 92172445Sassar if (rval) { 92272445Sassar char realm[REALM_SZ]; 92372445Sassar if((rval = krb_get_lrealm(realm, 1)) == KSUCCESS) 92472445Sassar rval = krb_verify_user(pw->pw_name, 92572445Sassar "", realm, 92672445Sassar passwd, 92772445Sassar KRB_VERIFY_SECURE, NULL); 92872445Sassar if (rval == KSUCCESS ) { 92972445Sassar chown (tkt_string(), pw->pw_uid, pw->pw_gid); 93072445Sassar if(k_hasafs()) 93172445Sassar krb_afslog(0, 0); 93272445Sassar } 93372445Sassar } 93455682Smarkm#endif 93572445Sassar if (rval) 93655682Smarkm rval = unix_verify_user(pw->pw_name, passwd); 93755682Smarkm } else { 938178825Sdfr#ifdef OTP 93955682Smarkm char *s; 94055682Smarkm if ((s = otp_error(&otp_ctx)) != NULL) 94155682Smarkm lreply(530, "OTP: %s", s); 94255682Smarkm#endif 94355682Smarkm } 94455682Smarkm memset (passwd, 0, strlen(passwd)); 94555682Smarkm 94655682Smarkm /* 94755682Smarkm * If rval == 1, the user failed the authentication 94855682Smarkm * check above. If rval == 0, either Kerberos or 94955682Smarkm * local authentication succeeded. 95055682Smarkm */ 95155682Smarkm if (rval) { 95255682Smarkm char data_addr[256]; 95355682Smarkm 95455682Smarkm if (inet_ntop (his_addr->sa_family, 95555682Smarkm socket_get_address(his_addr), 95655682Smarkm data_addr, sizeof(data_addr)) == NULL) 95755682Smarkm strlcpy (data_addr, "unknown address", 95855682Smarkm sizeof(data_addr)); 95955682Smarkm 96055682Smarkm reply(530, "Login incorrect."); 96155682Smarkm if (logging) 96255682Smarkm syslog(LOG_NOTICE, 96355682Smarkm "FTP LOGIN FAILED FROM %s(%s), %s", 96455682Smarkm remotehost, 96555682Smarkm data_addr, 96655682Smarkm curname); 96755682Smarkm pw = NULL; 96855682Smarkm if (login_attempts++ >= 5) { 96955682Smarkm syslog(LOG_NOTICE, 97055682Smarkm "repeated login failures from %s(%s)", 97155682Smarkm remotehost, 97255682Smarkm data_addr); 97355682Smarkm exit(0); 97455682Smarkm } 97555682Smarkm return; 97655682Smarkm } 97755682Smarkm } 97855682Smarkm if(!do_login(230, passwd)) 97955682Smarkm return; 98055682Smarkm 98155682Smarkm /* Forget all about it... */ 98255682Smarkm end_login(); 98355682Smarkm} 98455682Smarkm 98555682Smarkmvoid 98655682Smarkmretrieve(const char *cmd, char *name) 98755682Smarkm{ 98855682Smarkm FILE *fin = NULL, *dout; 98955682Smarkm struct stat st; 99055682Smarkm int (*closefunc) (FILE *); 99155682Smarkm char line[BUFSIZ]; 99255682Smarkm 99355682Smarkm 99455682Smarkm if (cmd == 0) { 99555682Smarkm fin = fopen(name, "r"); 99655682Smarkm closefunc = fclose; 99755682Smarkm st.st_size = 0; 99855682Smarkm if(fin == NULL){ 99955682Smarkm int save_errno = errno; 100055682Smarkm struct cmds { 100155682Smarkm const char *ext; 100255682Smarkm const char *cmd; 100355682Smarkm const char *rev_cmd; 100455682Smarkm } cmds[] = { 100555682Smarkm {".tar", "/bin/gtar cPf - %s", NULL}, 100655682Smarkm {".tar.gz", "/bin/gtar zcPf - %s", NULL}, 100755682Smarkm {".tar.Z", "/bin/gtar ZcPf - %s", NULL}, 100855682Smarkm {".gz", "/bin/gzip -c -- %s", "/bin/gzip -c -d -- %s"}, 100955682Smarkm {".Z", "/bin/compress -c -- %s", "/bin/uncompress -c -- %s"}, 101055682Smarkm {NULL, NULL} 101155682Smarkm }; 101255682Smarkm struct cmds *p; 101355682Smarkm for(p = cmds; p->ext; p++){ 101455682Smarkm char *tail = name + strlen(name) - strlen(p->ext); 101555682Smarkm char c = *tail; 101655682Smarkm 101755682Smarkm if(strcmp(tail, p->ext) == 0 && 101855682Smarkm (*tail = 0) == 0 && 101955682Smarkm access(name, R_OK) == 0){ 102055682Smarkm snprintf (line, sizeof(line), p->cmd, name); 102155682Smarkm *tail = c; 102255682Smarkm break; 102355682Smarkm } 102455682Smarkm *tail = c; 102555682Smarkm if (p->rev_cmd != NULL) { 102655682Smarkm char *ext; 1027178825Sdfr int ret; 102855682Smarkm 1029178825Sdfr ret = asprintf(&ext, "%s%s", name, p->ext); 1030178825Sdfr if (ret != -1) { 103155682Smarkm if (access(ext, R_OK) == 0) { 103255682Smarkm snprintf (line, sizeof(line), 103355682Smarkm p->rev_cmd, ext); 103455682Smarkm free(ext); 103555682Smarkm break; 103655682Smarkm } 103755682Smarkm free(ext); 103855682Smarkm } 103955682Smarkm } 104055682Smarkm 104155682Smarkm } 104255682Smarkm if(p->ext){ 104355682Smarkm fin = ftpd_popen(line, "r", 0, 0); 104455682Smarkm closefunc = ftpd_pclose; 104555682Smarkm st.st_size = -1; 104655682Smarkm cmd = line; 104755682Smarkm } else 104855682Smarkm errno = save_errno; 104955682Smarkm } 105055682Smarkm } else { 105155682Smarkm snprintf(line, sizeof(line), cmd, name); 105255682Smarkm name = line; 105355682Smarkm fin = ftpd_popen(line, "r", 1, 0); 105455682Smarkm closefunc = ftpd_pclose; 105555682Smarkm st.st_size = -1; 105655682Smarkm } 105755682Smarkm if (fin == NULL) { 105855682Smarkm if (errno != 0) { 105955682Smarkm perror_reply(550, name); 106055682Smarkm if (cmd == 0) { 106155682Smarkm LOGCMD("get", name); 106255682Smarkm } 106355682Smarkm } 106455682Smarkm return; 106555682Smarkm } 106655682Smarkm byte_count = -1; 106755682Smarkm if (cmd == 0){ 106855682Smarkm if(fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { 106955682Smarkm reply(550, "%s: not a plain file.", name); 107055682Smarkm goto done; 107155682Smarkm } 107255682Smarkm } 107355682Smarkm if (restart_point) { 107455682Smarkm if (type == TYPE_A) { 107555682Smarkm off_t i, n; 107655682Smarkm int c; 107755682Smarkm 107855682Smarkm n = restart_point; 107955682Smarkm i = 0; 108055682Smarkm while (i++ < n) { 108155682Smarkm if ((c=getc(fin)) == EOF) { 108255682Smarkm perror_reply(550, name); 108355682Smarkm goto done; 108455682Smarkm } 108555682Smarkm if (c == '\n') 108655682Smarkm i++; 108755682Smarkm } 108855682Smarkm } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { 108955682Smarkm perror_reply(550, name); 109055682Smarkm goto done; 109155682Smarkm } 109255682Smarkm } 109355682Smarkm dout = dataconn(name, st.st_size, "w"); 109455682Smarkm if (dout == NULL) 109555682Smarkm goto done; 109655682Smarkm set_buffer_size(fileno(dout), 0); 109755682Smarkm send_data(fin, dout); 109855682Smarkm fclose(dout); 109955682Smarkm data = -1; 110055682Smarkm pdata = -1; 110155682Smarkmdone: 110255682Smarkm if (cmd == 0) 110355682Smarkm LOGBYTES("get", name, byte_count); 110455682Smarkm (*closefunc)(fin); 110555682Smarkm} 110655682Smarkm 110755682Smarkm/* filename sanity check */ 110855682Smarkm 110955682Smarkmint 111055682Smarkmfilename_check(char *filename) 111155682Smarkm{ 1112178825Sdfr char *p; 111355682Smarkm 1114178825Sdfr p = strrchr(filename, '/'); 111555682Smarkm if(p) 111655682Smarkm filename = p + 1; 111755682Smarkm 111855682Smarkm p = filename; 111955682Smarkm 1120178825Sdfr if(isalnum((unsigned char)*p)){ 112155682Smarkm p++; 1122178825Sdfr while(*p && (isalnum((unsigned char)*p) || strchr(good_chars, (unsigned char)*p))) 112355682Smarkm p++; 112455682Smarkm if(*p == '\0') 112555682Smarkm return 0; 112655682Smarkm } 112772445Sassar lreply(553, "\"%s\" is not an acceptable filename.", filename); 112855682Smarkm lreply(553, "The filename must start with an alphanumeric " 112955682Smarkm "character and must only"); 113055682Smarkm reply(553, "consist of alphanumeric characters or any of the following: %s", 113155682Smarkm good_chars); 113255682Smarkm return 1; 113355682Smarkm} 113455682Smarkm 113555682Smarkmvoid 113655682Smarkmdo_store(char *name, char *mode, int unique) 113755682Smarkm{ 113855682Smarkm FILE *fout, *din; 113955682Smarkm struct stat st; 114055682Smarkm int (*closefunc) (FILE *); 114155682Smarkm 114255682Smarkm if(guest && filename_check(name)) 114355682Smarkm return; 114455682Smarkm if (unique && stat(name, &st) == 0 && 114555682Smarkm (name = gunique(name)) == NULL) { 114655682Smarkm LOGCMD(*mode == 'w' ? "put" : "append", name); 114755682Smarkm return; 114855682Smarkm } 114955682Smarkm 115055682Smarkm if (restart_point) 115155682Smarkm mode = "r+"; 115255682Smarkm fout = fopen(name, mode); 115355682Smarkm closefunc = fclose; 115455682Smarkm if (fout == NULL) { 115555682Smarkm perror_reply(553, name); 115655682Smarkm LOGCMD(*mode == 'w' ? "put" : "append", name); 115755682Smarkm return; 115855682Smarkm } 115955682Smarkm byte_count = -1; 116055682Smarkm if (restart_point) { 116155682Smarkm if (type == TYPE_A) { 116255682Smarkm off_t i, n; 116355682Smarkm int c; 116455682Smarkm 116555682Smarkm n = restart_point; 116655682Smarkm i = 0; 116755682Smarkm while (i++ < n) { 116855682Smarkm if ((c=getc(fout)) == EOF) { 116955682Smarkm perror_reply(550, name); 117055682Smarkm goto done; 117155682Smarkm } 117255682Smarkm if (c == '\n') 117355682Smarkm i++; 117455682Smarkm } 117555682Smarkm /* 117655682Smarkm * We must do this seek to "current" position 117755682Smarkm * because we are changing from reading to 117855682Smarkm * writing. 117955682Smarkm */ 118055682Smarkm if (fseek(fout, 0L, SEEK_CUR) < 0) { 118155682Smarkm perror_reply(550, name); 118255682Smarkm goto done; 118355682Smarkm } 118455682Smarkm } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) { 118555682Smarkm perror_reply(550, name); 118655682Smarkm goto done; 118755682Smarkm } 118855682Smarkm } 118955682Smarkm din = dataconn(name, (off_t)-1, "r"); 119055682Smarkm if (din == NULL) 119155682Smarkm goto done; 119255682Smarkm set_buffer_size(fileno(din), 1); 119355682Smarkm if (receive_data(din, fout) == 0) { 119478527Sassar if((*closefunc)(fout) < 0) 119578527Sassar perror_reply(552, name); 119678527Sassar else { 119755682Smarkm if (unique) 119855682Smarkm reply(226, "Transfer complete (unique file name:%s).", 119955682Smarkm name); 120055682Smarkm else 120155682Smarkm reply(226, "Transfer complete."); 120278527Sassar } 120378527Sassar } else 120478527Sassar (*closefunc)(fout); 120555682Smarkm fclose(din); 120655682Smarkm data = -1; 120755682Smarkm pdata = -1; 120855682Smarkmdone: 120955682Smarkm LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 121055682Smarkm} 121155682Smarkm 121255682Smarkmstatic FILE * 1213178825Sdfrgetdatasock(const char *mode, int domain) 121455682Smarkm{ 121555682Smarkm int s, t, tries; 121655682Smarkm 121755682Smarkm if (data >= 0) 121855682Smarkm return (fdopen(data, mode)); 1219178825Sdfr if (seteuid(0) < 0) 1220178825Sdfr fatal("Failed to seteuid"); 1221178825Sdfr s = socket(domain, SOCK_STREAM, 0); 122255682Smarkm if (s < 0) 122355682Smarkm goto bad; 122455682Smarkm socket_set_reuseaddr (s, 1); 122555682Smarkm /* anchor socket to avoid multi-homing problems */ 122655682Smarkm socket_set_address_and_port (data_source, 122755682Smarkm socket_get_address (ctrl_addr), 122855682Smarkm socket_get_port (data_source)); 122955682Smarkm 123055682Smarkm for (tries = 1; ; tries++) { 123155682Smarkm if (bind(s, data_source, 123255682Smarkm socket_sockaddr_size (data_source)) >= 0) 123355682Smarkm break; 123455682Smarkm if (errno != EADDRINUSE || tries > 10) 123555682Smarkm goto bad; 123655682Smarkm sleep(tries); 123755682Smarkm } 1238178825Sdfr if (seteuid(pw->pw_uid) < 0) 1239178825Sdfr fatal("Failed to seteuid"); 124055682Smarkm#ifdef IPTOS_THROUGHPUT 124155682Smarkm socket_set_tos (s, IPTOS_THROUGHPUT); 124255682Smarkm#endif 124355682Smarkm return (fdopen(s, mode)); 124455682Smarkmbad: 124555682Smarkm /* Return the real value of errno (close may change it) */ 124655682Smarkm t = errno; 1247178825Sdfr if (seteuid((uid_t)pw->pw_uid) < 0) 1248178825Sdfr fatal("Failed to seteuid"); 124955682Smarkm close(s); 125055682Smarkm errno = t; 125155682Smarkm return (NULL); 125255682Smarkm} 125355682Smarkm 125490926Snectarstatic int 125590926Snectaraccept_with_timeout(int socket, 125690926Snectar struct sockaddr *address, 1257120945Snectar socklen_t *address_len, 125890926Snectar struct timeval *timeout) 125990926Snectar{ 126090926Snectar int ret; 126190926Snectar fd_set rfd; 126290926Snectar FD_ZERO(&rfd); 126390926Snectar FD_SET(socket, &rfd); 126490926Snectar ret = select(socket + 1, &rfd, NULL, NULL, timeout); 126590926Snectar if(ret < 0) 126690926Snectar return ret; 126790926Snectar if(ret == 0) { 126890926Snectar errno = ETIMEDOUT; 126990926Snectar return -1; 127090926Snectar } 127190926Snectar return accept(socket, address, address_len); 127290926Snectar} 127390926Snectar 127455682Smarkmstatic FILE * 127555682Smarkmdataconn(const char *name, off_t size, const char *mode) 127655682Smarkm{ 127755682Smarkm char sizebuf[32]; 127855682Smarkm FILE *file; 1279178825Sdfr int domain, retry = 0; 128055682Smarkm 128155682Smarkm file_size = size; 128255682Smarkm byte_count = 0; 128355682Smarkm if (size >= 0) 128455682Smarkm snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", (long)size); 128555682Smarkm else 128655682Smarkm *sizebuf = '\0'; 128755682Smarkm if (pdata >= 0) { 128855682Smarkm struct sockaddr_storage from_ss; 128955682Smarkm struct sockaddr *from = (struct sockaddr *)&from_ss; 129090926Snectar struct timeval timeout; 129155682Smarkm int s; 129272445Sassar socklen_t fromlen = sizeof(from_ss); 129355682Smarkm 129490926Snectar timeout.tv_sec = 15; 129590926Snectar timeout.tv_usec = 0; 129690926Snectar s = accept_with_timeout(pdata, from, &fromlen, &timeout); 129755682Smarkm if (s < 0) { 129855682Smarkm reply(425, "Can't open data connection."); 129955682Smarkm close(pdata); 130055682Smarkm pdata = -1; 130155682Smarkm return (NULL); 130255682Smarkm } 130355682Smarkm close(pdata); 130455682Smarkm pdata = s; 130555682Smarkm#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) 130655682Smarkm { 130755682Smarkm int tos = IPTOS_THROUGHPUT; 130855682Smarkm 130955682Smarkm setsockopt(s, IPPROTO_IP, IP_TOS, (void *)&tos, 131055682Smarkm sizeof(tos)); 131155682Smarkm } 131255682Smarkm#endif 131355682Smarkm reply(150, "Opening %s mode data connection for '%s'%s.", 131455682Smarkm type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 131555682Smarkm return (fdopen(pdata, mode)); 131655682Smarkm } 131755682Smarkm if (data >= 0) { 131855682Smarkm reply(125, "Using existing data connection for '%s'%s.", 131955682Smarkm name, sizebuf); 132055682Smarkm usedefault = 1; 132155682Smarkm return (fdopen(data, mode)); 132255682Smarkm } 132355682Smarkm if (usedefault) 132455682Smarkm data_dest = his_addr; 132555682Smarkm usedefault = 1; 1326178825Sdfr /* 1327178825Sdfr * Default to using the same socket type as the ctrl address, 1328178825Sdfr * unless we know the type of the data address. 1329178825Sdfr */ 1330178825Sdfr domain = data_dest->sa_family; 1331178825Sdfr if (domain == PF_UNSPEC) 1332178825Sdfr domain = ctrl_addr->sa_family; 1333178825Sdfr 1334178825Sdfr file = getdatasock(mode, domain); 133555682Smarkm if (file == NULL) { 133655682Smarkm char data_addr[256]; 133755682Smarkm 133855682Smarkm if (inet_ntop (data_source->sa_family, 133955682Smarkm socket_get_address(data_source), 134055682Smarkm data_addr, sizeof(data_addr)) == NULL) 134155682Smarkm strlcpy (data_addr, "unknown address", 134255682Smarkm sizeof(data_addr)); 134355682Smarkm 134455682Smarkm reply(425, "Can't create data socket (%s,%d): %s.", 134555682Smarkm data_addr, 134655682Smarkm socket_get_port (data_source), 134755682Smarkm strerror(errno)); 134855682Smarkm return (NULL); 134955682Smarkm } 135055682Smarkm data = fileno(file); 135155682Smarkm while (connect(data, data_dest, 135255682Smarkm socket_sockaddr_size(data_dest)) < 0) { 135355682Smarkm if (errno == EADDRINUSE && retry < swaitmax) { 135455682Smarkm sleep(swaitint); 135555682Smarkm retry += swaitint; 135655682Smarkm continue; 135755682Smarkm } 135855682Smarkm perror_reply(425, "Can't build data connection"); 135955682Smarkm fclose(file); 136055682Smarkm data = -1; 136155682Smarkm return (NULL); 136255682Smarkm } 136355682Smarkm reply(150, "Opening %s mode data connection for '%s'%s.", 136455682Smarkm type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 136555682Smarkm return (file); 136655682Smarkm} 136755682Smarkm 136855682Smarkm/* 136955682Smarkm * Tranfer the contents of "instr" to "outstr" peer using the appropriate 137055682Smarkm * encapsulation of the data subject * to Mode, Structure, and Type. 137155682Smarkm * 137255682Smarkm * NB: Form isn't handled. 137355682Smarkm */ 137455682Smarkmstatic void 137555682Smarkmsend_data(FILE *instr, FILE *outstr) 137655682Smarkm{ 137755682Smarkm int c, cnt, filefd, netfd; 137855682Smarkm static char *buf; 137955682Smarkm static size_t bufsize; 138055682Smarkm 1381142403Snectar transflag = 1; 138255682Smarkm switch (type) { 138355682Smarkm 138455682Smarkm case TYPE_A: 138555682Smarkm while ((c = getc(instr)) != EOF) { 1386142403Snectar if (urgflag && handleoobcmd()) 1387142403Snectar return; 138855682Smarkm byte_count++; 138955682Smarkm if(c == '\n') 139055682Smarkm sec_putc('\r', outstr); 139155682Smarkm sec_putc(c, outstr); 139255682Smarkm } 139355682Smarkm sec_fflush(outstr); 139455682Smarkm transflag = 0; 1395142403Snectar urgflag = 0; 139655682Smarkm if (ferror(instr)) 139755682Smarkm goto file_err; 139855682Smarkm if (ferror(outstr)) 139955682Smarkm goto data_err; 140055682Smarkm reply(226, "Transfer complete."); 140155682Smarkm return; 140255682Smarkm 140355682Smarkm case TYPE_I: 140455682Smarkm case TYPE_L: 1405142403Snectar#if 0 /* XXX handle urg flag */ 140655682Smarkm#if defined(HAVE_MMAP) && !defined(NO_MMAP) 140755682Smarkm#ifndef MAP_FAILED 140855682Smarkm#define MAP_FAILED (-1) 140955682Smarkm#endif 141055682Smarkm { 141155682Smarkm struct stat st; 141255682Smarkm char *chunk; 141355682Smarkm int in = fileno(instr); 141455682Smarkm if(fstat(in, &st) == 0 && S_ISREG(st.st_mode) 141555682Smarkm && st.st_size > 0) { 141655682Smarkm /* 141755682Smarkm * mmap zero bytes has potential of loosing, don't do it. 141855682Smarkm */ 141955682Smarkm chunk = mmap(0, st.st_size, PROT_READ, 142055682Smarkm MAP_SHARED, in, 0); 142155682Smarkm if((void *)chunk != (void *)MAP_FAILED) { 142255682Smarkm cnt = st.st_size - restart_point; 142355682Smarkm sec_write(fileno(outstr), chunk + restart_point, cnt); 142455682Smarkm if (munmap(chunk, st.st_size) < 0) 142555682Smarkm warn ("munmap"); 142655682Smarkm sec_fflush(outstr); 142755682Smarkm byte_count = cnt; 142855682Smarkm transflag = 0; 1429142403Snectar urgflag = 0; 143055682Smarkm } 143155682Smarkm } 143255682Smarkm } 143355682Smarkm#endif 1434142403Snectar#endif 143555682Smarkm if(transflag) { 143655682Smarkm struct stat st; 143755682Smarkm 143855682Smarkm netfd = fileno(outstr); 143955682Smarkm filefd = fileno(instr); 144055682Smarkm buf = alloc_buffer (buf, &bufsize, 144155682Smarkm fstat(filefd, &st) >= 0 ? &st : NULL); 144255682Smarkm if (buf == NULL) { 144355682Smarkm transflag = 0; 1444142403Snectar urgflag = 0; 144555682Smarkm perror_reply(451, "Local resource failure: malloc"); 144655682Smarkm return; 144755682Smarkm } 144855682Smarkm while ((cnt = read(filefd, buf, bufsize)) > 0 && 1449142403Snectar sec_write(netfd, buf, cnt) == cnt) { 145055682Smarkm byte_count += cnt; 1451142403Snectar if (urgflag && handleoobcmd()) 1452142403Snectar return; 1453142403Snectar } 145455682Smarkm sec_fflush(outstr); /* to end an encrypted stream */ 145555682Smarkm transflag = 0; 1456142403Snectar urgflag = 0; 145755682Smarkm if (cnt != 0) { 145855682Smarkm if (cnt < 0) 145955682Smarkm goto file_err; 146055682Smarkm goto data_err; 146155682Smarkm } 146255682Smarkm } 146355682Smarkm reply(226, "Transfer complete."); 146455682Smarkm return; 146555682Smarkm default: 146655682Smarkm transflag = 0; 1467142403Snectar urgflag = 0; 146855682Smarkm reply(550, "Unimplemented TYPE %d in send_data", type); 146955682Smarkm return; 147055682Smarkm } 147155682Smarkm 147255682Smarkmdata_err: 147355682Smarkm transflag = 0; 1474142403Snectar urgflag = 0; 147555682Smarkm perror_reply(426, "Data connection"); 147655682Smarkm return; 147755682Smarkm 147855682Smarkmfile_err: 147955682Smarkm transflag = 0; 1480142403Snectar urgflag = 0; 148155682Smarkm perror_reply(551, "Error on input file"); 148255682Smarkm} 148355682Smarkm 148455682Smarkm/* 148555682Smarkm * Transfer data from peer to "outstr" using the appropriate encapulation of 148655682Smarkm * the data subject to Mode, Structure, and Type. 148755682Smarkm * 148855682Smarkm * N.B.: Form isn't handled. 148955682Smarkm */ 149055682Smarkmstatic int 149155682Smarkmreceive_data(FILE *instr, FILE *outstr) 149255682Smarkm{ 149355682Smarkm int cnt, bare_lfs = 0; 149455682Smarkm static char *buf; 149555682Smarkm static size_t bufsize; 149655682Smarkm struct stat st; 149755682Smarkm 1498142403Snectar transflag = 1; 149955682Smarkm 150055682Smarkm buf = alloc_buffer (buf, &bufsize, 150155682Smarkm fstat(fileno(outstr), &st) >= 0 ? &st : NULL); 150255682Smarkm if (buf == NULL) { 150355682Smarkm transflag = 0; 1504142403Snectar urgflag = 0; 150555682Smarkm perror_reply(451, "Local resource failure: malloc"); 150655682Smarkm return -1; 150755682Smarkm } 150855682Smarkm 150955682Smarkm switch (type) { 151055682Smarkm 151155682Smarkm case TYPE_I: 151255682Smarkm case TYPE_L: 151355682Smarkm while ((cnt = sec_read(fileno(instr), buf, bufsize)) > 0) { 151455682Smarkm if (write(fileno(outstr), buf, cnt) != cnt) 151555682Smarkm goto file_err; 151655682Smarkm byte_count += cnt; 1517142403Snectar if (urgflag && handleoobcmd()) 1518142403Snectar return (-1); 151955682Smarkm } 152055682Smarkm if (cnt < 0) 152155682Smarkm goto data_err; 152255682Smarkm transflag = 0; 1523142403Snectar urgflag = 0; 152455682Smarkm return (0); 152555682Smarkm 152655682Smarkm case TYPE_E: 152755682Smarkm reply(553, "TYPE E not implemented."); 152855682Smarkm transflag = 0; 1529142403Snectar urgflag = 0; 153055682Smarkm return (-1); 153155682Smarkm 153255682Smarkm case TYPE_A: 153355682Smarkm { 153455682Smarkm char *p, *q; 153555682Smarkm int cr_flag = 0; 153655682Smarkm while ((cnt = sec_read(fileno(instr), 153755682Smarkm buf + cr_flag, 153855682Smarkm bufsize - cr_flag)) > 0){ 1539142403Snectar if (urgflag && handleoobcmd()) 1540142403Snectar return (-1); 154155682Smarkm byte_count += cnt; 154255682Smarkm cnt += cr_flag; 154355682Smarkm cr_flag = 0; 154455682Smarkm for(p = buf, q = buf; p < buf + cnt;) { 154555682Smarkm if(*p == '\n') 154655682Smarkm bare_lfs++; 154755682Smarkm if(*p == '\r') { 154855682Smarkm if(p == buf + cnt - 1){ 154955682Smarkm cr_flag = 1; 155055682Smarkm p++; 155155682Smarkm continue; 155255682Smarkm }else if(p[1] == '\n'){ 155355682Smarkm *q++ = '\n'; 155455682Smarkm p += 2; 155555682Smarkm continue; 155655682Smarkm } 155755682Smarkm } 155855682Smarkm *q++ = *p++; 155955682Smarkm } 156055682Smarkm fwrite(buf, q - buf, 1, outstr); 156155682Smarkm if(cr_flag) 156255682Smarkm buf[0] = '\r'; 156355682Smarkm } 156455682Smarkm if(cr_flag) 156555682Smarkm putc('\r', outstr); 156655682Smarkm fflush(outstr); 156755682Smarkm if (ferror(instr)) 156855682Smarkm goto data_err; 156955682Smarkm if (ferror(outstr)) 157055682Smarkm goto file_err; 157155682Smarkm transflag = 0; 1572142403Snectar urgflag = 0; 157355682Smarkm if (bare_lfs) { 157455682Smarkm lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n" 157555682Smarkm " File may not have transferred correctly.\r\n", 157655682Smarkm bare_lfs); 157755682Smarkm } 157855682Smarkm return (0); 157955682Smarkm } 158055682Smarkm default: 158155682Smarkm reply(550, "Unimplemented TYPE %d in receive_data", type); 158255682Smarkm transflag = 0; 1583142403Snectar urgflag = 0; 158455682Smarkm return (-1); 158555682Smarkm } 158655682Smarkm 158755682Smarkmdata_err: 158855682Smarkm transflag = 0; 1589142403Snectar urgflag = 0; 159055682Smarkm perror_reply(426, "Data Connection"); 159155682Smarkm return (-1); 159255682Smarkm 159355682Smarkmfile_err: 159455682Smarkm transflag = 0; 1595142403Snectar urgflag = 0; 159655682Smarkm perror_reply(452, "Error writing file"); 159755682Smarkm return (-1); 159855682Smarkm} 159955682Smarkm 160055682Smarkmvoid 160155682Smarkmstatfilecmd(char *filename) 160255682Smarkm{ 160355682Smarkm FILE *fin; 160455682Smarkm int c; 160555682Smarkm char line[LINE_MAX]; 160655682Smarkm 160755682Smarkm snprintf(line, sizeof(line), "/bin/ls -la -- %s", filename); 160855682Smarkm fin = ftpd_popen(line, "r", 1, 0); 160955682Smarkm lreply(211, "status of %s:", filename); 161055682Smarkm while ((c = getc(fin)) != EOF) { 161155682Smarkm if (c == '\n') { 161255682Smarkm if (ferror(stdout)){ 161355682Smarkm perror_reply(421, "control connection"); 161455682Smarkm ftpd_pclose(fin); 161555682Smarkm dologout(1); 161655682Smarkm /* NOTREACHED */ 161755682Smarkm } 161855682Smarkm if (ferror(fin)) { 161955682Smarkm perror_reply(551, filename); 162055682Smarkm ftpd_pclose(fin); 162155682Smarkm return; 162255682Smarkm } 162355682Smarkm putc('\r', stdout); 162455682Smarkm } 162555682Smarkm putc(c, stdout); 162655682Smarkm } 162755682Smarkm ftpd_pclose(fin); 162855682Smarkm reply(211, "End of Status"); 162955682Smarkm} 163055682Smarkm 163155682Smarkmvoid 163255682Smarkmstatcmd(void) 163355682Smarkm{ 163455682Smarkm#if 0 163555682Smarkm struct sockaddr_in *sin; 163655682Smarkm u_char *a, *p; 163755682Smarkm 163855682Smarkm lreply(211, "%s FTP server (%s) status:", hostname, version); 163955682Smarkm printf(" %s\r\n", version); 164055682Smarkm printf(" Connected to %s", remotehost); 1641178825Sdfr if (!isdigit((unsigned char)remotehost[0])) 164255682Smarkm printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 164355682Smarkm printf("\r\n"); 164455682Smarkm if (logged_in) { 164555682Smarkm if (guest) 164655682Smarkm printf(" Logged in anonymously\r\n"); 164755682Smarkm else 164855682Smarkm printf(" Logged in as %s\r\n", pw->pw_name); 164955682Smarkm } else if (askpasswd) 165055682Smarkm printf(" Waiting for password\r\n"); 165155682Smarkm else 165255682Smarkm printf(" Waiting for user name\r\n"); 165355682Smarkm printf(" TYPE: %s", typenames[type]); 165455682Smarkm if (type == TYPE_A || type == TYPE_E) 165555682Smarkm printf(", FORM: %s", formnames[form]); 165655682Smarkm if (type == TYPE_L) 165755682Smarkm#if NBBY == 8 165855682Smarkm printf(" %d", NBBY); 165955682Smarkm#else 166055682Smarkm printf(" %d", bytesize); /* need definition! */ 166155682Smarkm#endif 166255682Smarkm printf("; STRUcture: %s; transfer MODE: %s\r\n", 166355682Smarkm strunames[stru], modenames[mode]); 166455682Smarkm if (data != -1) 166555682Smarkm printf(" Data connection open\r\n"); 166655682Smarkm else if (pdata != -1) { 166755682Smarkm printf(" in Passive mode"); 166855682Smarkm sin = &pasv_addr; 166955682Smarkm goto printaddr; 167055682Smarkm } else if (usedefault == 0) { 167155682Smarkm printf(" PORT"); 167255682Smarkm sin = &data_dest; 167355682Smarkmprintaddr: 167455682Smarkm a = (u_char *) &sin->sin_addr; 167555682Smarkm p = (u_char *) &sin->sin_port; 167655682Smarkm#define UC(b) (((int) b) & 0xff) 167755682Smarkm printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 167855682Smarkm UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 167955682Smarkm#undef UC 168055682Smarkm } else 168155682Smarkm printf(" No data connection\r\n"); 168255682Smarkm#endif 168355682Smarkm reply(211, "End of status"); 168455682Smarkm} 168555682Smarkm 168655682Smarkmvoid 168755682Smarkmfatal(char *s) 168855682Smarkm{ 168955682Smarkm 169055682Smarkm reply(451, "Error in server: %s\n", s); 169155682Smarkm reply(221, "Closing connection due to server error."); 169255682Smarkm dologout(0); 169355682Smarkm /* NOTREACHED */ 169455682Smarkm} 169555682Smarkm 169655682Smarkmstatic void 169755682Smarkmint_reply(int, char *, const char *, va_list) 169855682Smarkm#ifdef __GNUC__ 169955682Smarkm__attribute__ ((format (printf, 3, 0))) 170055682Smarkm#endif 170155682Smarkm; 170255682Smarkm 170355682Smarkmstatic void 170455682Smarkmint_reply(int n, char *c, const char *fmt, va_list ap) 170555682Smarkm{ 170655682Smarkm char buf[10240]; 170755682Smarkm char *p; 170855682Smarkm p=buf; 170955682Smarkm if(n){ 171055682Smarkm snprintf(p, sizeof(buf), "%d%s", n, c); 171155682Smarkm p+=strlen(p); 171255682Smarkm } 171355682Smarkm vsnprintf(p, sizeof(buf) - strlen(p), fmt, ap); 171455682Smarkm p+=strlen(p); 171555682Smarkm snprintf(p, sizeof(buf) - strlen(p), "\r\n"); 171655682Smarkm p+=strlen(p); 171755682Smarkm sec_fprintf(stdout, "%s", buf); 171855682Smarkm fflush(stdout); 171955682Smarkm if (debug) 172055682Smarkm syslog(LOG_DEBUG, "<--- %s- ", buf); 172155682Smarkm} 172255682Smarkm 172355682Smarkmvoid 172455682Smarkmreply(int n, const char *fmt, ...) 172555682Smarkm{ 172655682Smarkm va_list ap; 172755682Smarkm va_start(ap, fmt); 172855682Smarkm int_reply(n, " ", fmt, ap); 172955682Smarkm delete_ftp_command(); 173055682Smarkm va_end(ap); 173155682Smarkm} 173255682Smarkm 173355682Smarkmvoid 173455682Smarkmlreply(int n, const char *fmt, ...) 173555682Smarkm{ 173655682Smarkm va_list ap; 173755682Smarkm va_start(ap, fmt); 173855682Smarkm int_reply(n, "-", fmt, ap); 173955682Smarkm va_end(ap); 174055682Smarkm} 174155682Smarkm 174255682Smarkmvoid 174355682Smarkmnreply(const char *fmt, ...) 174455682Smarkm{ 174555682Smarkm va_list ap; 174655682Smarkm va_start(ap, fmt); 174755682Smarkm int_reply(0, NULL, fmt, ap); 174855682Smarkm va_end(ap); 174955682Smarkm} 175055682Smarkm 175155682Smarkmstatic void 175255682Smarkmack(char *s) 175355682Smarkm{ 175455682Smarkm 175555682Smarkm reply(250, "%s command successful.", s); 175655682Smarkm} 175755682Smarkm 175855682Smarkmvoid 175955682Smarkmnack(char *s) 176055682Smarkm{ 176155682Smarkm 176255682Smarkm reply(502, "%s command not implemented.", s); 176355682Smarkm} 176455682Smarkm 176555682Smarkmvoid 176655682Smarkmdo_delete(char *name) 176755682Smarkm{ 176855682Smarkm struct stat st; 176955682Smarkm 177055682Smarkm LOGCMD("delete", name); 177155682Smarkm if (stat(name, &st) < 0) { 177255682Smarkm perror_reply(550, name); 177355682Smarkm return; 177455682Smarkm } 177555682Smarkm if ((st.st_mode&S_IFMT) == S_IFDIR) { 177655682Smarkm if (rmdir(name) < 0) { 177755682Smarkm perror_reply(550, name); 177855682Smarkm return; 177955682Smarkm } 178055682Smarkm goto done; 178155682Smarkm } 178255682Smarkm if (unlink(name) < 0) { 178355682Smarkm perror_reply(550, name); 178455682Smarkm return; 178555682Smarkm } 178655682Smarkmdone: 178755682Smarkm ack("DELE"); 178855682Smarkm} 178955682Smarkm 179055682Smarkmvoid 179155682Smarkmcwd(char *path) 179255682Smarkm{ 179355682Smarkm 179455682Smarkm if (chdir(path) < 0) 179555682Smarkm perror_reply(550, path); 179655682Smarkm else 179755682Smarkm ack("CWD"); 179855682Smarkm} 179955682Smarkm 180055682Smarkmvoid 180155682Smarkmmakedir(char *name) 180255682Smarkm{ 180355682Smarkm 180455682Smarkm LOGCMD("mkdir", name); 180555682Smarkm if(guest && filename_check(name)) 180655682Smarkm return; 180755682Smarkm if (mkdir(name, 0777) < 0) 180855682Smarkm perror_reply(550, name); 180955682Smarkm else{ 181055682Smarkm if(guest) 181155682Smarkm chmod(name, 0700); /* guest has umask 777 */ 181255682Smarkm reply(257, "MKD command successful."); 181355682Smarkm } 181455682Smarkm} 181555682Smarkm 181655682Smarkmvoid 181755682Smarkmremovedir(char *name) 181855682Smarkm{ 181955682Smarkm 182055682Smarkm LOGCMD("rmdir", name); 182155682Smarkm if (rmdir(name) < 0) 182255682Smarkm perror_reply(550, name); 182355682Smarkm else 182455682Smarkm ack("RMD"); 182555682Smarkm} 182655682Smarkm 182755682Smarkmvoid 182855682Smarkmpwd(void) 182955682Smarkm{ 183055682Smarkm char path[MaxPathLen]; 183155682Smarkm char *ret; 183255682Smarkm 183355682Smarkm /* SunOS has a broken getcwd that does popen(pwd) (!!!), this 183455682Smarkm * failes miserably when running chroot 183555682Smarkm */ 183655682Smarkm ret = getcwd(path, sizeof(path)); 183755682Smarkm if (ret == NULL) 183855682Smarkm reply(550, "%s.", strerror(errno)); 183955682Smarkm else 184055682Smarkm reply(257, "\"%s\" is current directory.", path); 184155682Smarkm} 184255682Smarkm 184355682Smarkmchar * 184455682Smarkmrenamefrom(char *name) 184555682Smarkm{ 184655682Smarkm struct stat st; 184755682Smarkm 184855682Smarkm if (stat(name, &st) < 0) { 184955682Smarkm perror_reply(550, name); 185055682Smarkm return NULL; 185155682Smarkm } 185255682Smarkm reply(350, "File exists, ready for destination name"); 185355682Smarkm return (name); 185455682Smarkm} 185555682Smarkm 185655682Smarkmvoid 185755682Smarkmrenamecmd(char *from, char *to) 185855682Smarkm{ 185955682Smarkm 186055682Smarkm LOGCMD2("rename", from, to); 186155682Smarkm if(guest && filename_check(to)) 186255682Smarkm return; 186355682Smarkm if (rename(from, to) < 0) 186455682Smarkm perror_reply(550, "rename"); 186555682Smarkm else 186655682Smarkm ack("RNTO"); 186755682Smarkm} 186855682Smarkm 186955682Smarkmstatic void 187055682Smarkmdolog(struct sockaddr *sa, int len) 187155682Smarkm{ 187255682Smarkm getnameinfo_verified (sa, len, remotehost, sizeof(remotehost), 187355682Smarkm NULL, 0, 0); 187455682Smarkm#ifdef HAVE_SETPROCTITLE 187555682Smarkm snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 187664593Skris setproctitle("%s", proctitle); 187755682Smarkm#endif /* HAVE_SETPROCTITLE */ 187855682Smarkm 187955682Smarkm if (logging) { 188055682Smarkm char data_addr[256]; 188155682Smarkm 188255682Smarkm if (inet_ntop (his_addr->sa_family, 188355682Smarkm socket_get_address(his_addr), 188455682Smarkm data_addr, sizeof(data_addr)) == NULL) 188555682Smarkm strlcpy (data_addr, "unknown address", 188655682Smarkm sizeof(data_addr)); 188755682Smarkm 188855682Smarkm 188955682Smarkm syslog(LOG_INFO, "connection from %s(%s)", 189055682Smarkm remotehost, 189155682Smarkm data_addr); 189255682Smarkm } 189355682Smarkm} 189455682Smarkm 189555682Smarkm/* 189655682Smarkm * Record logout in wtmp file 189755682Smarkm * and exit with supplied status. 189855682Smarkm */ 189955682Smarkmvoid 190055682Smarkmdologout(int status) 190155682Smarkm{ 190255682Smarkm transflag = 0; 1903142403Snectar urgflag = 0; 190455682Smarkm if (logged_in) { 1905178825Sdfr#if KRB4 || KRB5 190655682Smarkm cond_kdestroy(); 190755682Smarkm#endif 1908178825Sdfr seteuid((uid_t)0); /* No need to check, we call exit() below */ 1909178825Sdfr ftpd_logwtmp(ttyline, "", ""); 191055682Smarkm } 191155682Smarkm /* beware of flushing buffers after a SIGPIPE */ 191255682Smarkm#ifdef XXX 191355682Smarkm exit(status); 191455682Smarkm#else 191555682Smarkm _exit(status); 191655682Smarkm#endif 191755682Smarkm} 191855682Smarkm 191955682Smarkmvoid abor(void) 192055682Smarkm{ 1921142403Snectar if (!transflag) 1922142403Snectar return; 1923142403Snectar reply(426, "Transfer aborted. Data connection closed."); 1924142403Snectar reply(226, "Abort successful"); 1925142403Snectar transflag = 0; 192655682Smarkm} 192755682Smarkm 192855682Smarkmstatic void 192955682Smarkmmyoob(int signo) 193055682Smarkm{ 1931142403Snectar urgflag = 1; 1932142403Snectar} 1933142403Snectar 1934142403Snectarstatic char * 1935142403Snectarmec_space(char *p) 1936142403Snectar{ 1937142403Snectar while(isspace(*(unsigned char *)p)) 1938142403Snectar p++; 1939142403Snectar return p; 1940142403Snectar} 1941142403Snectar 1942142403Snectarstatic int 1943142403Snectarhandleoobcmd(void) 1944142403Snectar{ 194555682Smarkm char *cp; 194655682Smarkm 194755682Smarkm /* only process if transfer occurring */ 194855682Smarkm if (!transflag) 1949142403Snectar return 0; 195055682Smarkm 1951142403Snectar urgflag = 0; 195255682Smarkm 195355682Smarkm cp = tmpline; 1954142403Snectar if (ftpd_getline(cp, sizeof(tmpline)) == NULL) { 195555682Smarkm reply(221, "You could at least say goodbye."); 195655682Smarkm dologout(0); 195755682Smarkm } 1958142403Snectar 1959142403Snectar if (strncasecmp("MIC", cp, 3) == 0) { 1960142403Snectar mec(mec_space(cp + 3), prot_safe); 1961142403Snectar } else if (strncasecmp("CONF", cp, 4) == 0) { 1962142403Snectar mec(mec_space(cp + 4), prot_confidential); 1963142403Snectar } else if (strncasecmp("ENC", cp, 3) == 0) { 1964142403Snectar mec(mec_space(cp + 3), prot_private); 1965142403Snectar } else if (!allow_insecure_oob) { 1966142403Snectar reply(533, "Command protection level denied " 1967142403Snectar "for paranoid reasons."); 1968142403Snectar goto out; 196955682Smarkm } 1970142403Snectar 1971142403Snectar if (secure_command()) 1972142403Snectar cp = ftp_command; 1973142403Snectar 1974142403Snectar if (strcasecmp(cp, "ABOR\r\n") == 0) { 1975142403Snectar abor(); 1976142403Snectar } else if (strcasecmp(cp, "STAT\r\n") == 0) { 197755682Smarkm if (file_size != (off_t) -1) 197855682Smarkm reply(213, "Status: %ld of %ld bytes transferred", 197955682Smarkm (long)byte_count, 198055682Smarkm (long)file_size); 198155682Smarkm else 1982142403Snectar reply(213, "Status: %ld bytes transferred", 198355682Smarkm (long)byte_count); 198455682Smarkm } 1985142403Snectarout: 1986142403Snectar return (transflag == 0); 198755682Smarkm} 198855682Smarkm 198955682Smarkm/* 199055682Smarkm * Note: a response of 425 is not mentioned as a possible response to 199155682Smarkm * the PASV command in RFC959. However, it has been blessed as 199255682Smarkm * a legitimate response by Jon Postel in a telephone conversation 199355682Smarkm * with Rick Adams on 25 Jan 89. 199455682Smarkm */ 199555682Smarkmvoid 199655682Smarkmpasv(void) 199755682Smarkm{ 199872445Sassar socklen_t len; 199955682Smarkm char *p, *a; 200055682Smarkm struct sockaddr_in *sin; 200155682Smarkm 200255682Smarkm if (ctrl_addr->sa_family != AF_INET) { 200355682Smarkm reply(425, 200455682Smarkm "You cannot do PASV with something that's not IPv4"); 200555682Smarkm return; 200655682Smarkm } 200755682Smarkm 200872445Sassar if(pdata != -1) 200972445Sassar close(pdata); 201072445Sassar 201155682Smarkm pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0); 201255682Smarkm if (pdata < 0) { 201355682Smarkm perror_reply(425, "Can't open passive connection"); 201455682Smarkm return; 201555682Smarkm } 201655682Smarkm pasv_addr->sa_family = ctrl_addr->sa_family; 201755682Smarkm socket_set_address_and_port (pasv_addr, 201855682Smarkm socket_get_address (ctrl_addr), 201955682Smarkm 0); 202090926Snectar socket_set_portrange(pdata, restricted_data_ports, 202190926Snectar pasv_addr->sa_family); 2022178825Sdfr if (seteuid(0) < 0) 2023178825Sdfr fatal("Failed to seteuid"); 202455682Smarkm if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) { 2025178825Sdfr if (seteuid(pw->pw_uid) < 0) 2026178825Sdfr fatal("Failed to seteuid"); 202755682Smarkm goto pasv_error; 202855682Smarkm } 2029178825Sdfr if (seteuid(pw->pw_uid) < 0) 2030178825Sdfr fatal("Failed to seteuid"); 203155682Smarkm len = sizeof(pasv_addr_ss); 203255682Smarkm if (getsockname(pdata, pasv_addr, &len) < 0) 203355682Smarkm goto pasv_error; 203455682Smarkm if (listen(pdata, 1) < 0) 203555682Smarkm goto pasv_error; 203655682Smarkm sin = (struct sockaddr_in *)pasv_addr; 203755682Smarkm a = (char *) &sin->sin_addr; 203855682Smarkm p = (char *) &sin->sin_port; 203955682Smarkm 204055682Smarkm#define UC(b) (((int) b) & 0xff) 204155682Smarkm 204255682Smarkm reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 204355682Smarkm UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 204455682Smarkm return; 204555682Smarkm 204655682Smarkmpasv_error: 204755682Smarkm close(pdata); 204855682Smarkm pdata = -1; 204955682Smarkm perror_reply(425, "Can't open passive connection"); 205055682Smarkm return; 205155682Smarkm} 205255682Smarkm 205355682Smarkmvoid 205455682Smarkmepsv(char *proto) 205555682Smarkm{ 205672445Sassar socklen_t len; 205755682Smarkm 205855682Smarkm pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0); 205955682Smarkm if (pdata < 0) { 206055682Smarkm perror_reply(425, "Can't open passive connection"); 206155682Smarkm return; 206255682Smarkm } 206355682Smarkm pasv_addr->sa_family = ctrl_addr->sa_family; 206455682Smarkm socket_set_address_and_port (pasv_addr, 206555682Smarkm socket_get_address (ctrl_addr), 206655682Smarkm 0); 206790926Snectar socket_set_portrange(pdata, restricted_data_ports, 206890926Snectar pasv_addr->sa_family); 2069178825Sdfr if (seteuid(0) < 0) 2070178825Sdfr fatal("Failed to seteuid"); 207155682Smarkm if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) { 2072178825Sdfr if (seteuid(pw->pw_uid)) 2073178825Sdfr fatal("Failed to seteuid"); 207455682Smarkm goto pasv_error; 207555682Smarkm } 2076178825Sdfr if (seteuid(pw->pw_uid) < 0) 2077178825Sdfr fatal("Failed to seteuid"); 207855682Smarkm len = sizeof(pasv_addr_ss); 207955682Smarkm if (getsockname(pdata, pasv_addr, &len) < 0) 208055682Smarkm goto pasv_error; 208155682Smarkm if (listen(pdata, 1) < 0) 208255682Smarkm goto pasv_error; 208355682Smarkm 208455682Smarkm reply(229, "Entering Extended Passive Mode (|||%d|)", 208555682Smarkm ntohs(socket_get_port (pasv_addr))); 208655682Smarkm return; 208755682Smarkm 208855682Smarkmpasv_error: 208955682Smarkm close(pdata); 209055682Smarkm pdata = -1; 209155682Smarkm perror_reply(425, "Can't open passive connection"); 209255682Smarkm return; 209355682Smarkm} 209455682Smarkm 209555682Smarkmvoid 209655682Smarkmeprt(char *str) 209755682Smarkm{ 209855682Smarkm char *end; 209955682Smarkm char sep; 210055682Smarkm int af; 210155682Smarkm int ret; 210255682Smarkm int port; 210355682Smarkm 210455682Smarkm usedefault = 0; 210555682Smarkm if (pdata >= 0) { 210655682Smarkm close(pdata); 210755682Smarkm pdata = -1; 210855682Smarkm } 210955682Smarkm 211055682Smarkm sep = *str++; 211155682Smarkm if (sep == '\0') { 211255682Smarkm reply(500, "Bad syntax in EPRT"); 211355682Smarkm return; 211455682Smarkm } 211555682Smarkm af = strtol (str, &end, 0); 211655682Smarkm if (af == 0 || *end != sep) { 211755682Smarkm reply(500, "Bad syntax in EPRT"); 211855682Smarkm return; 211955682Smarkm } 212055682Smarkm str = end + 1; 212155682Smarkm switch (af) { 212255682Smarkm#ifdef HAVE_IPV6 212355682Smarkm case 2 : 212455682Smarkm data_dest->sa_family = AF_INET6; 212555682Smarkm break; 212655682Smarkm#endif 212755682Smarkm case 1 : 212855682Smarkm data_dest->sa_family = AF_INET; 212955682Smarkm break; 213055682Smarkm default : 213155682Smarkm reply(522, "Network protocol %d not supported, use (1" 213255682Smarkm#ifdef HAVE_IPV6 213355682Smarkm ",2" 213455682Smarkm#endif 213555682Smarkm ")", af); 213655682Smarkm return; 213755682Smarkm } 213855682Smarkm end = strchr (str, sep); 213955682Smarkm if (end == NULL) { 214055682Smarkm reply(500, "Bad syntax in EPRT"); 214155682Smarkm return; 214255682Smarkm } 214355682Smarkm *end = '\0'; 214455682Smarkm ret = inet_pton (data_dest->sa_family, str, 214555682Smarkm socket_get_address (data_dest)); 214655682Smarkm 214755682Smarkm if (ret != 1) { 214855682Smarkm reply(500, "Bad address syntax in EPRT"); 214955682Smarkm return; 215055682Smarkm } 215155682Smarkm str = end + 1; 215255682Smarkm port = strtol (str, &end, 0); 215355682Smarkm if (port == 0 || *end != sep) { 215455682Smarkm reply(500, "Bad port syntax in EPRT"); 215555682Smarkm return; 215655682Smarkm } 215755682Smarkm socket_set_port (data_dest, htons(port)); 215855682Smarkm reply(200, "EPRT command successful."); 215955682Smarkm} 216055682Smarkm 216155682Smarkm/* 216255682Smarkm * Generate unique name for file with basename "local". 216355682Smarkm * The file named "local" is already known to exist. 216455682Smarkm * Generates failure reply on error. 216555682Smarkm */ 216655682Smarkmstatic char * 216755682Smarkmgunique(char *local) 216855682Smarkm{ 216955682Smarkm static char new[MaxPathLen]; 217055682Smarkm struct stat st; 217155682Smarkm int count; 217255682Smarkm char *cp; 217355682Smarkm 217455682Smarkm cp = strrchr(local, '/'); 217555682Smarkm if (cp) 217655682Smarkm *cp = '\0'; 217755682Smarkm if (stat(cp ? local : ".", &st) < 0) { 217855682Smarkm perror_reply(553, cp ? local : "."); 217955682Smarkm return NULL; 218055682Smarkm } 218155682Smarkm if (cp) 218255682Smarkm *cp = '/'; 218355682Smarkm for (count = 1; count < 100; count++) { 218455682Smarkm snprintf (new, sizeof(new), "%s.%d", local, count); 218555682Smarkm if (stat(new, &st) < 0) 218655682Smarkm return (new); 218755682Smarkm } 218855682Smarkm reply(452, "Unique file name cannot be created."); 218955682Smarkm return (NULL); 219055682Smarkm} 219155682Smarkm 219255682Smarkm/* 219355682Smarkm * Format and send reply containing system error number. 219455682Smarkm */ 219555682Smarkmvoid 219655682Smarkmperror_reply(int code, const char *string) 219755682Smarkm{ 219855682Smarkm reply(code, "%s: %s.", string, strerror(errno)); 219955682Smarkm} 220055682Smarkm 220155682Smarkmstatic char *onefile[] = { 220255682Smarkm "", 220355682Smarkm 0 220455682Smarkm}; 220555682Smarkm 220655682Smarkmvoid 220755682Smarkmlist_file(char *file) 220855682Smarkm{ 220955682Smarkm if(use_builtin_ls) { 221055682Smarkm FILE *dout; 221155682Smarkm dout = dataconn(file, -1, "w"); 221255682Smarkm if (dout == NULL) 221355682Smarkm return; 221455682Smarkm set_buffer_size(fileno(dout), 0); 2215102644Snectar if(builtin_ls(dout, file) == 0) 2216102644Snectar reply(226, "Transfer complete."); 2217102644Snectar else 2218102644Snectar reply(451, "Requested action aborted. Local error in processing."); 221955682Smarkm fclose(dout); 222055682Smarkm data = -1; 222155682Smarkm pdata = -1; 222255682Smarkm } else { 222355682Smarkm#ifdef HAVE_LS_A 222472445Sassar const char *cmd = "/bin/ls -lA %s"; 222555682Smarkm#else 222672445Sassar const char *cmd = "/bin/ls -la %s"; 222755682Smarkm#endif 222855682Smarkm retrieve(cmd, file); 222955682Smarkm } 223055682Smarkm} 223155682Smarkm 223255682Smarkmvoid 223355682Smarkmsend_file_list(char *whichf) 223455682Smarkm{ 2235142403Snectar struct stat st; 2236142403Snectar DIR *dirp = NULL; 2237142403Snectar struct dirent *dir; 2238142403Snectar FILE *dout = NULL; 2239142403Snectar char **dirlist, *dirname; 2240142403Snectar int simple = 0; 2241142403Snectar int freeglob = 0; 2242142403Snectar glob_t gl; 2243142403Snectar char buf[MaxPathLen]; 224455682Smarkm 2245142403Snectar if (strpbrk(whichf, "~{[*?") != NULL) { 2246142403Snectar int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE| 224790926Snectar#ifdef GLOB_MAXPATH 2248142403Snectar GLOB_MAXPATH 224990926Snectar#else 2250142403Snectar GLOB_LIMIT 225190926Snectar#endif 2252142403Snectar ; 225355682Smarkm 2254142403Snectar memset(&gl, 0, sizeof(gl)); 2255142403Snectar freeglob = 1; 2256142403Snectar if (glob(whichf, flags, 0, &gl)) { 2257142403Snectar reply(550, "not found"); 2258142403Snectar goto out; 2259142403Snectar } else if (gl.gl_pathc == 0) { 2260142403Snectar errno = ENOENT; 2261142403Snectar perror_reply(550, whichf); 2262142403Snectar goto out; 2263142403Snectar } 2264142403Snectar dirlist = gl.gl_pathv; 2265142403Snectar } else { 2266142403Snectar onefile[0] = whichf; 2267142403Snectar dirlist = onefile; 2268142403Snectar simple = 1; 226955682Smarkm } 227055682Smarkm 2271142403Snectar while ((dirname = *dirlist++)) { 227255682Smarkm 2273142403Snectar if (urgflag && handleoobcmd()) 2274142403Snectar goto out; 227555682Smarkm 2276142403Snectar if (stat(dirname, &st) < 0) { 2277142403Snectar /* 2278142403Snectar * If user typed "ls -l", etc, and the client 2279142403Snectar * used NLST, do what the user meant. 2280142403Snectar */ 2281142403Snectar if (dirname[0] == '-' && *dirlist == NULL && 2282142403Snectar transflag == 0) { 2283142403Snectar list_file(dirname); 2284142403Snectar goto out; 2285142403Snectar } 2286142403Snectar perror_reply(550, whichf); 2287142403Snectar goto out; 2288142403Snectar } 228955682Smarkm 2290142403Snectar if (S_ISREG(st.st_mode)) { 2291142403Snectar if (dout == NULL) { 2292142403Snectar dout = dataconn("file list", (off_t)-1, "w"); 2293142403Snectar if (dout == NULL) 2294142403Snectar goto out; 2295142403Snectar transflag = 1; 2296142403Snectar } 2297142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", dirname, 2298142403Snectar type == TYPE_A ? "\r" : ""); 2299142403Snectar sec_write(fileno(dout), buf, strlen(buf)); 2300142403Snectar byte_count += strlen(dirname) + 1; 2301142403Snectar continue; 2302142403Snectar } else if (!S_ISDIR(st.st_mode)) 2303142403Snectar continue; 230455682Smarkm 2305142403Snectar if ((dirp = opendir(dirname)) == NULL) 2306142403Snectar continue; 230755682Smarkm 2308142403Snectar while ((dir = readdir(dirp)) != NULL) { 2309142403Snectar char nbuf[MaxPathLen]; 231055682Smarkm 2311142403Snectar if (urgflag && handleoobcmd()) 2312142403Snectar goto out; 2313142403Snectar 2314142403Snectar if (!strcmp(dir->d_name, ".")) 2315142403Snectar continue; 2316142403Snectar if (!strcmp(dir->d_name, "..")) 2317142403Snectar continue; 2318142403Snectar 2319142403Snectar snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); 2320142403Snectar 2321142403Snectar /* 2322142403Snectar * We have to do a stat to insure it's 2323142403Snectar * not a directory or special file. 2324142403Snectar */ 2325142403Snectar if (simple || (stat(nbuf, &st) == 0 && 2326142403Snectar S_ISREG(st.st_mode))) { 2327142403Snectar if (dout == NULL) { 2328142403Snectar dout = dataconn("file list", (off_t)-1, "w"); 2329142403Snectar if (dout == NULL) 2330142403Snectar goto out; 2331142403Snectar transflag = 1; 2332142403Snectar } 2333142403Snectar if(strncmp(nbuf, "./", 2) == 0) 2334142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2, 2335142403Snectar type == TYPE_A ? "\r" : ""); 2336142403Snectar else 2337142403Snectar snprintf(buf, sizeof(buf), "%s%s\n", nbuf, 2338142403Snectar type == TYPE_A ? "\r" : ""); 2339142403Snectar sec_write(fileno(dout), buf, strlen(buf)); 2340142403Snectar byte_count += strlen(nbuf) + 1; 2341142403Snectar } 234255682Smarkm } 2343142403Snectar closedir(dirp); 234455682Smarkm } 2345142403Snectar if (dout == NULL) 2346142403Snectar reply(550, "No files found."); 2347142403Snectar else if (ferror(dout) != 0) 2348142403Snectar perror_reply(550, "Data connection"); 2349142403Snectar else 2350142403Snectar reply(226, "Transfer complete."); 235155682Smarkm 2352142403Snectarout: 2353142403Snectar transflag = 0; 2354142403Snectar if (dout != NULL){ 2355142403Snectar sec_write(fileno(dout), buf, 0); /* XXX flush */ 235655682Smarkm 2357142403Snectar fclose(dout); 2358142403Snectar } 2359142403Snectar data = -1; 2360142403Snectar pdata = -1; 2361142403Snectar if (freeglob) { 2362142403Snectar freeglob = 0; 2363142403Snectar globfree(&gl); 2364142403Snectar } 236555682Smarkm} 236655682Smarkm 236755682Smarkm 236855682Smarkmint 236955682Smarkmfind(char *pattern) 237055682Smarkm{ 237155682Smarkm char line[1024]; 237255682Smarkm FILE *f; 237355682Smarkm 237455682Smarkm snprintf(line, sizeof(line), 237555682Smarkm "/bin/locate -d %s -- %s", 237655682Smarkm ftp_rooted("/etc/locatedb"), 237755682Smarkm pattern); 237855682Smarkm f = ftpd_popen(line, "r", 1, 1); 237955682Smarkm if(f == NULL){ 238055682Smarkm perror_reply(550, "/bin/locate"); 238155682Smarkm return 1; 238255682Smarkm } 238355682Smarkm lreply(200, "Output from find."); 238455682Smarkm while(fgets(line, sizeof(line), f)){ 238555682Smarkm if(line[strlen(line)-1] == '\n') 238655682Smarkm line[strlen(line)-1] = 0; 238755682Smarkm nreply("%s", line); 238855682Smarkm } 238955682Smarkm reply(200, "Done"); 239055682Smarkm ftpd_pclose(f); 239155682Smarkm return 0; 239255682Smarkm} 239355682Smarkm 2394