Path: blob/main/crypto/heimdal/appl/ftp/ftpd/ftpd.c
34907 views
/*1* Copyright (c) 1985, 1988, 1990, 1992, 1993, 19942* The Regents of the University of California. All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. All advertising materials mentioning features or use of this software13* must display the following acknowledgement:14* This product includes software developed by the University of15* California, Berkeley and its contributors.16* 4. Neither the name of the University nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#define FTP_NAMES34#include "ftpd_locl.h"35#ifdef KRB536#include <krb5.h>37#endif38#include "getarg.h"3940RCSID("$Id$");4142static char version[] = "Version 6.00";4344extern off_t restart_point;45extern char cbuf[];4647struct sockaddr_storage ctrl_addr_ss;48struct sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss;4950struct sockaddr_storage data_source_ss;51struct sockaddr *data_source = (struct sockaddr *)&data_source_ss;5253struct sockaddr_storage data_dest_ss;54struct sockaddr *data_dest = (struct sockaddr *)&data_dest_ss;5556struct sockaddr_storage his_addr_ss;57struct sockaddr *his_addr = (struct sockaddr *)&his_addr_ss;5859struct sockaddr_storage pasv_addr_ss;60struct sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss;6162int data;63int logged_in;64struct passwd *pw;65int debug = 0;66int ftpd_timeout = 900; /* timeout after 15 minutes of inactivity */67int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */68int restricted_data_ports = 1;69int logging;70int guest;71int dochroot;72int type;73int form;74int stru; /* avoid C keyword */75int mode;76int usedefault = 1; /* for data transfers */77int pdata = -1; /* for passive mode */78int allow_insecure_oob = 1;79static int transflag;80static int urgflag;81off_t file_size;82off_t byte_count;83#if !defined(CMASK) || CMASK == 084#undef CMASK85#define CMASK 02786#endif87int defumask = CMASK; /* default umask value */88int guest_umask = 0777; /* Paranoia for anonymous users */89char tmpline[10240];90char hostname[MaxHostNameLen];91char remotehost[MaxHostNameLen];92static char ttyline[20];93int paranoid = 1;9495#define AUTH_PLAIN (1 << 0) /* allow sending passwords */96#define AUTH_OTP (1 << 1) /* passwords are one-time */97#define AUTH_FTP (1 << 2) /* allow anonymous login */9899static int auth_level = 0; /* Only allow kerberos login by default */100101/*102* Timeout intervals for retrying connections103* to hosts that don't accept PORT cmds. This104* is a kludge, but given the problems with TCP...105*/106#define SWAITMAX 90 /* wait at most 90 seconds */107#define SWAITINT 5 /* interval between retries */108109int swaitmax = SWAITMAX;110int swaitint = SWAITINT;111112#ifdef HAVE_SETPROCTITLE113char proctitle[BUFSIZ]; /* initial part of title */114#endif /* HAVE_SETPROCTITLE */115116#define LOGCMD(cmd, file) \117if (logging > 1) \118syslog(LOG_INFO,"%s %s%s", cmd, \119*(file) == '/' ? "" : curdir(), file);120#define LOGCMD2(cmd, file1, file2) \121if (logging > 1) \122syslog(LOG_INFO,"%s %s%s %s%s", cmd, \123*(file1) == '/' ? "" : curdir(), file1, \124*(file2) == '/' ? "" : curdir(), file2);125#define LOGBYTES(cmd, file, cnt) \126if (logging > 1) { \127if (cnt == (off_t)-1) \128syslog(LOG_INFO,"%s %s%s", cmd, \129*(file) == '/' ? "" : curdir(), file); \130else \131syslog(LOG_INFO, "%s %s%s = %ld bytes", \132cmd, (*(file) == '/') ? "" : curdir(), file, (long)cnt); \133}134135static void ack (char *);136static void myoob (int);137static int handleoobcmd(void);138static int checkuser (char *, char *);139static int checkaccess (char *);140static FILE *dataconn (const char *, off_t, const char *);141static void dolog (struct sockaddr *, int);142static void end_login (void);143static FILE *getdatasock (const char *, int);144static char *gunique (char *);145static RETSIGTYPE lostconn (int);146static int receive_data (FILE *, FILE *);147static void send_data (FILE *, FILE *);148static struct passwd * sgetpwnam (char *);149150static char *151curdir(void)152{153static char path[MaxPathLen+1]; /* path + '/' + '\0' */154155if (getcwd(path, sizeof(path)-1) == NULL)156return ("");157if (path[1] != '\0') /* special case for root dir. */158strlcat(path, "/", sizeof(path));159/* For guest account, skip / since it's chrooted */160return (guest ? path+1 : path);161}162163#ifndef LINE_MAX164#define LINE_MAX 1024165#endif166167static int168parse_auth_level(char *str)169{170char *p;171int ret = 0;172char *foo = NULL;173174for(p = strtok_r(str, ",", &foo);175p;176p = strtok_r(NULL, ",", &foo)) {177if(strcmp(p, "user") == 0)178;179#ifdef OTP180else if(strcmp(p, "otp") == 0)181ret |= AUTH_PLAIN|AUTH_OTP;182#endif183else if(strcmp(p, "ftp") == 0 ||184strcmp(p, "safe") == 0)185ret |= AUTH_FTP;186else if(strcmp(p, "plain") == 0)187ret |= AUTH_PLAIN;188else if(strcmp(p, "none") == 0)189ret |= AUTH_PLAIN|AUTH_FTP;190else191warnx("bad value for -a: `%s'", p);192}193return ret;194}195196/*197* Print usage and die.198*/199200static int interactive_flag;201static char *guest_umask_string;202static char *port_string;203static char *umask_string;204static char *auth_string;205206int use_builtin_ls = -1;207208static int help_flag;209static int version_flag;210211static const char *good_chars = "+-=_,.";212213struct getargs args[] = {214{ NULL, 'a', arg_string, &auth_string, "required authentication" },215{ NULL, 'i', arg_flag, &interactive_flag, "don't assume stdin is a socket" },216{ NULL, 'p', arg_string, &port_string, "what port to listen to" },217{ NULL, 'g', arg_string, &guest_umask_string, "umask for guest logins" },218{ NULL, 'l', arg_counter, &logging, "log more stuff", "" },219{ NULL, 't', arg_integer, &ftpd_timeout, "initial timeout" },220{ NULL, 'T', arg_integer, &maxtimeout, "max timeout" },221{ NULL, 'u', arg_string, &umask_string, "umask for user logins" },222{ NULL, 'U', arg_negative_flag, &restricted_data_ports, "don't use high data ports" },223{ NULL, 'd', arg_flag, &debug, "enable debugging" },224{ NULL, 'v', arg_flag, &debug, "enable debugging" },225{ "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" },226{ "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" },227{ "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" },228#ifdef KRB5229{ "gss-bindings", 0, arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL},230#endif231{ "version", 0, arg_flag, &version_flag },232{ "help", 'h', arg_flag, &help_flag }233};234235static int num_args = sizeof(args) / sizeof(args[0]);236237static void238usage (int code)239{240arg_printusage(args, num_args, NULL, "");241exit (code);242}243244/* output contents of a file */245static int246show_file(const char *file, int code)247{248FILE *f;249char buf[128];250251f = fopen(file, "r");252if(f == NULL)253return -1;254while(fgets(buf, sizeof(buf), f)){255buf[strcspn(buf, "\r\n")] = '\0';256lreply(code, "%s", buf);257}258fclose(f);259return 0;260}261262int263main(int argc, char **argv)264{265socklen_t his_addr_len, ctrl_addr_len;266int on = 1;267int port;268struct servent *sp;269270int optind = 0;271272setprogname (argv[0]);273274if(getarg(args, num_args, argc, argv, &optind))275usage(1);276277if(help_flag)278usage(0);279280if(version_flag) {281print_version(NULL);282exit(0);283}284285if(auth_string)286auth_level = parse_auth_level(auth_string);287{288char *p;289long val = 0;290291if(guest_umask_string) {292val = strtol(guest_umask_string, &p, 8);293if (*p != '\0' || val < 0)294warnx("bad value for -g");295else296guest_umask = val;297}298if(umask_string) {299val = strtol(umask_string, &p, 8);300if (*p != '\0' || val < 0)301warnx("bad value for -u");302else303defumask = val;304}305}306sp = getservbyname("ftp", "tcp");307if(sp)308port = sp->s_port;309else310port = htons(21);311if(port_string) {312sp = getservbyname(port_string, "tcp");313if(sp)314port = sp->s_port;315else316if(isdigit((unsigned char)port_string[0]))317port = htons(atoi(port_string));318else319warnx("bad value for -p");320}321322if (maxtimeout < ftpd_timeout)323maxtimeout = ftpd_timeout;324325#if 0326if (ftpd_timeout > maxtimeout)327ftpd_timeout = maxtimeout;328#endif329330if(interactive_flag)331mini_inetd(port, NULL);332333/*334* LOG_NDELAY sets up the logging connection immediately,335* necessary for anonymous ftp's that chroot and can't do it later.336*/337openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);338his_addr_len = sizeof(his_addr_ss);339if (getpeername(STDIN_FILENO, his_addr, &his_addr_len) < 0) {340syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);341exit(1);342}343ctrl_addr_len = sizeof(ctrl_addr_ss);344if (getsockname(STDIN_FILENO, ctrl_addr, &ctrl_addr_len) < 0) {345syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);346exit(1);347}348#if defined(IP_TOS)349if (ctrl_addr->sa_family == AF_INET)350socket_set_tos(STDIN_FILENO, IP_TOS);351#endif352data_source->sa_family = ctrl_addr->sa_family;353socket_set_port (data_source,354htons(ntohs(socket_get_port(ctrl_addr)) - 1));355356/* set this here so it can be put in wtmp */357snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid());358359360/* freopen(_PATH_DEVNULL, "w", stderr); */361signal(SIGPIPE, lostconn);362signal(SIGCHLD, SIG_IGN);363#ifdef SIGURG364if (signal(SIGURG, myoob) == SIG_ERR)365syslog(LOG_ERR, "signal: %m");366#endif367368/* Try to handle urgent data inline */369#if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT)370if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (void *)&on,371sizeof(on)) < 0)372syslog(LOG_ERR, "setsockopt: %m");373#endif374375#ifdef F_SETOWN376if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)377syslog(LOG_ERR, "fcntl F_SETOWN: %m");378#endif379dolog(his_addr, his_addr_len);380/*381* Set up default state382*/383data = -1;384type = TYPE_A;385form = FORM_N;386stru = STRU_F;387mode = MODE_S;388tmpline[0] = '\0';389390/* If logins are disabled, print out the message. */391if(show_file(_PATH_NOLOGIN, 530) == 0) {392reply(530, "System not available.");393exit(0);394}395show_file(_PATH_FTPWELCOME, 220);396/* reply(220,) must follow */397gethostname(hostname, sizeof(hostname));398399reply(220, "%s FTP server (%s"400#ifdef KRB5401"+%s"402#endif403") ready.", hostname, version404#ifdef KRB5405,heimdal_version406#endif407);408409for (;;)410yyparse();411/* NOTREACHED */412}413414static RETSIGTYPE415lostconn(int signo)416{417418if (debug)419syslog(LOG_DEBUG, "lost connection");420dologout(-1);421}422423/*424* Helper function for sgetpwnam().425*/426static char *427sgetsave(char *s)428{429char *new = strdup(s);430431if (new == NULL) {432perror_reply(421, "Local resource failure: malloc");433dologout(1);434/* NOTREACHED */435}436return new;437}438439/*440* Save the result of a getpwnam. Used for USER command, since441* the data returned must not be clobbered by any other command442* (e.g., globbing).443*/444static struct passwd *445sgetpwnam(char *name)446{447static struct passwd save;448struct passwd *p;449450if ((p = k_getpwnam(name)) == NULL)451return (p);452if (save.pw_name) {453free(save.pw_name);454free(save.pw_passwd);455free(save.pw_gecos);456free(save.pw_dir);457free(save.pw_shell);458}459save = *p;460save.pw_name = sgetsave(p->pw_name);461save.pw_passwd = sgetsave(p->pw_passwd);462save.pw_gecos = sgetsave(p->pw_gecos);463save.pw_dir = sgetsave(p->pw_dir);464save.pw_shell = sgetsave(p->pw_shell);465return (&save);466}467468static int login_attempts; /* number of failed login attempts */469static int askpasswd; /* had user command, ask for passwd */470static char curname[10]; /* current USER name */471#ifdef OTP472OtpContext otp_ctx;473#endif474475/*476* USER command.477* Sets global passwd pointer pw if named account exists and is acceptable;478* sets askpasswd if a PASS command is expected. If logged in previously,479* need to reset state. If name is "ftp" or "anonymous", the name is not in480* _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.481* If account doesn't exist, ask for passwd anyway. Otherwise, check user482* requesting login privileges. Disallow anyone who does not have a standard483* shell as returned by getusershell(). Disallow anyone mentioned in the file484* _PATH_FTPUSERS to allow people such as root and uucp to be avoided.485*/486void487user(char *name)488{489char *cp, *shell;490491if(auth_level == 0 && !sec_complete){492reply(530, "No login allowed without authorization.");493return;494}495496if (logged_in) {497if (guest) {498reply(530, "Can't change user from guest login.");499return;500} else if (dochroot) {501reply(530, "Can't change user from chroot user.");502return;503}504end_login();505}506507guest = 0;508if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {509if ((auth_level & AUTH_FTP) == 0 ||510checkaccess("ftp") ||511checkaccess("anonymous"))512reply(530, "User %s access denied.", name);513else if ((pw = sgetpwnam("ftp")) != NULL) {514guest = 1;515defumask = guest_umask; /* paranoia for incoming */516askpasswd = 1;517reply(331, "Guest login ok, type your name as password.");518} else519reply(530, "User %s unknown.", name);520if (!askpasswd && logging) {521char data_addr[256];522523if (inet_ntop (his_addr->sa_family,524socket_get_address(his_addr),525data_addr, sizeof(data_addr)) == NULL)526strlcpy (data_addr, "unknown address",527sizeof(data_addr));528529syslog(LOG_NOTICE,530"ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)",531remotehost, data_addr);532}533return;534}535if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){536reply(530, "Only authorized and anonymous login allowed.");537return;538}539if ((pw = sgetpwnam(name))) {540if ((shell = pw->pw_shell) == NULL || *shell == 0)541shell = _PATH_BSHELL;542while ((cp = getusershell()) != NULL)543if (strcmp(cp, shell) == 0)544break;545endusershell();546547if (cp == NULL || checkaccess(name)) {548reply(530, "User %s access denied.", name);549if (logging) {550char data_addr[256];551552if (inet_ntop (his_addr->sa_family,553socket_get_address(his_addr),554data_addr,555sizeof(data_addr)) == NULL)556strlcpy (data_addr,557"unknown address",558sizeof(data_addr));559560syslog(LOG_NOTICE,561"FTP LOGIN REFUSED FROM %s(%s), %s",562remotehost,563data_addr,564name);565}566pw = (struct passwd *) NULL;567return;568}569}570if (logging)571strlcpy(curname, name, sizeof(curname));572if(sec_complete) {573if(sec_userok(name) == 0) {574do_login(232, name);575sec_session(name);576} else577reply(530, "User %s access denied.", name);578} else {579#ifdef OTP580char ss[256];581582if (otp_challenge(&otp_ctx, name, ss, sizeof(ss)) == 0) {583reply(331, "Password %s for %s required.",584ss, name);585askpasswd = 1;586} else587#endif588if ((auth_level & AUTH_OTP) == 0) {589reply(331, "Password required for %s.", name);590askpasswd = 1;591} else {592#ifdef OTP593char *s;594595if ((s = otp_error (&otp_ctx)) != NULL)596lreply(530, "OTP: %s", s);597#endif598reply(530,599"Only authorized, anonymous"600#ifdef OTP601" and OTP "602#endif603"login allowed.");604}605606}607/*608* Delay before reading passwd after first failed609* attempt to slow down passwd-guessing programs.610*/611if (login_attempts)612sleep(login_attempts);613}614615/*616* Check if a user is in the file "fname"617*/618static int619checkuser(char *fname, char *name)620{621FILE *fd;622int found = 0;623char *p, line[BUFSIZ];624625if ((fd = fopen(fname, "r")) != NULL) {626while (fgets(line, sizeof(line), fd) != NULL)627if ((p = strchr(line, '\n')) != NULL) {628*p = '\0';629if (line[0] == '#')630continue;631if (strcmp(line, name) == 0) {632found = 1;633break;634}635}636fclose(fd);637}638return (found);639}640641642/*643* Determine whether a user has access, based on information in644* _PATH_FTPUSERS. The users are listed one per line, with `allow'645* or `deny' after the username. If anything other than `allow', or646* just nothing, is given after the username, `deny' is assumed.647*648* If the user is not found in the file, but the pseudo-user `*' is,649* the permission is taken from that line.650*651* This preserves the old semantics where if a user was listed in the652* file he was denied, otherwise he was allowed.653*654* Return 1 if the user is denied, or 0 if he is allowed. */655656static int657match(const char *pattern, const char *string)658{659return fnmatch(pattern, string, FNM_NOESCAPE);660}661662static int663checkaccess(char *name)664{665#define ALLOWED 0666#define NOT_ALLOWED 1667FILE *fd;668int allowed = ALLOWED;669char *user, *perm, line[BUFSIZ];670char *foo;671672fd = fopen(_PATH_FTPUSERS, "r");673674if(fd == NULL)675return allowed;676677while (fgets(line, sizeof(line), fd) != NULL) {678foo = NULL;679user = strtok_r(line, " \t\n", &foo);680if (user == NULL || user[0] == '#')681continue;682perm = strtok_r(NULL, " \t\n", &foo);683if (match(user, name) == 0){684if(perm && strcmp(perm, "allow") == 0)685allowed = ALLOWED;686else687allowed = NOT_ALLOWED;688break;689}690}691fclose(fd);692return allowed;693}694#undef ALLOWED695#undef NOT_ALLOWED696697698int do_login(int code, char *passwd)699{700login_attempts = 0; /* this time successful */701if (setegid((gid_t)pw->pw_gid) < 0) {702reply(550, "Can't set gid.");703return -1;704}705initgroups(pw->pw_name, pw->pw_gid);706#if defined(KRB5)707if(k_hasafs())708k_setpag();709#endif710711/* open wtmp before chroot */712ftpd_logwtmp(ttyline, pw->pw_name, remotehost);713logged_in = 1;714715dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);716if (guest) {717/*718* We MUST do a chdir() after the chroot. Otherwise719* the old current directory will be accessible as "."720* outside the new root!721*/722if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {723reply(550, "Can't set guest privileges.");724return -1;725}726} else if (dochroot) {727if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {728reply(550, "Can't change root.");729return -1;730}731} else if (chdir(pw->pw_dir) < 0) {732if (chdir("/") < 0) {733reply(530, "User %s: can't change directory to %s.",734pw->pw_name, pw->pw_dir);735return -1;736} else737lreply(code, "No directory! Logging in with home=/");738}739if (seteuid((uid_t)pw->pw_uid) < 0) {740reply(550, "Can't set uid.");741return -1;742}743744if(use_builtin_ls == -1) {745struct stat st;746/* if /bin/ls exist and is a regular file, use it, otherwise747use built-in ls */748if(stat("/bin/ls", &st) == 0 &&749S_ISREG(st.st_mode))750use_builtin_ls = 0;751else752use_builtin_ls = 1;753}754755/*756* Display a login message, if it exists.757* N.B. reply(code,) must follow the message.758*/759show_file(_PATH_FTPLOGINMESG, code);760if(show_file(_PATH_ISSUE_NET, code) != 0)761show_file(_PATH_ISSUE, code);762if (guest) {763reply(code, "Guest login ok, access restrictions apply.");764#ifdef HAVE_SETPROCTITLE765snprintf (proctitle, sizeof(proctitle),766"%s: anonymous/%s",767remotehost,768passwd);769setproctitle("%s", proctitle);770#endif /* HAVE_SETPROCTITLE */771if (logging) {772char data_addr[256];773774if (inet_ntop (his_addr->sa_family,775socket_get_address(his_addr),776data_addr, sizeof(data_addr)) == NULL)777strlcpy (data_addr, "unknown address",778sizeof(data_addr));779780syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s",781remotehost,782data_addr,783passwd);784}785} else {786reply(code, "User %s logged in.", pw->pw_name);787#ifdef HAVE_SETPROCTITLE788snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);789setproctitle("%s", proctitle);790#endif /* HAVE_SETPROCTITLE */791if (logging) {792char data_addr[256];793794if (inet_ntop (his_addr->sa_family,795socket_get_address(his_addr),796data_addr, sizeof(data_addr)) == NULL)797strlcpy (data_addr, "unknown address",798sizeof(data_addr));799800syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s",801remotehost,802data_addr,803pw->pw_name);804}805}806umask(defumask);807return 0;808}809810/*811* Terminate login as previous user, if any, resetting state;812* used when USER command is given or login fails.813*/814static void815end_login(void)816{817818if (seteuid((uid_t)0) < 0)819fatal("Failed to seteuid");820if (logged_in)821ftpd_logwtmp(ttyline, "", "");822pw = NULL;823logged_in = 0;824guest = 0;825dochroot = 0;826}827828#ifdef KRB5829static int830krb5_verify(struct passwd *pwd, char *passwd)831{832krb5_context context;833krb5_ccache id;834krb5_principal princ;835krb5_error_code ret;836837ret = krb5_init_context(&context);838if(ret)839return ret;840841ret = krb5_parse_name(context, pwd->pw_name, &princ);842if(ret){843krb5_free_context(context);844return ret;845}846ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);847if(ret){848krb5_free_principal(context, princ);849krb5_free_context(context);850return ret;851}852ret = krb5_verify_user(context,853princ,854id,855passwd,8561,857NULL);858krb5_free_principal(context, princ);859if (k_hasafs()) {860krb5_afslog_uid_home(context, id,NULL, NULL,pwd->pw_uid, pwd->pw_dir);861}862krb5_cc_destroy(context, id);863krb5_free_context (context);864if(ret)865return ret;866return 0;867}868#endif /* KRB5 */869870void871pass(char *passwd)872{873int rval;874875/* some clients insists on sending a password */876if (logged_in && askpasswd == 0){877reply(230, "Password not necessary");878return;879}880881if (logged_in || askpasswd == 0) {882reply(503, "Login with USER first.");883return;884}885askpasswd = 0;886rval = 1;887if (!guest) { /* "ftp" is only account allowed no password */888if (pw == NULL)889rval = 1; /* failure below */890#ifdef OTP891else if (otp_verify_user (&otp_ctx, passwd) == 0) {892rval = 0;893}894#endif895else if((auth_level & AUTH_OTP) == 0) {896#ifdef KRB5897rval = krb5_verify(pw, passwd);898#endif899if (rval)900rval = unix_verify_user(pw->pw_name, passwd);901} else {902#ifdef OTP903char *s;904if ((s = otp_error(&otp_ctx)) != NULL)905lreply(530, "OTP: %s", s);906#endif907}908memset (passwd, 0, strlen(passwd));909910/*911* If rval == 1, the user failed the authentication912* check above. If rval == 0, either Kerberos or913* local authentication succeeded.914*/915if (rval) {916char data_addr[256];917918if (inet_ntop (his_addr->sa_family,919socket_get_address(his_addr),920data_addr, sizeof(data_addr)) == NULL)921strlcpy (data_addr, "unknown address",922sizeof(data_addr));923924reply(530, "Login incorrect.");925if (logging)926syslog(LOG_NOTICE,927"FTP LOGIN FAILED FROM %s(%s), %s",928remotehost,929data_addr,930curname);931pw = NULL;932if (login_attempts++ >= 5) {933syslog(LOG_NOTICE,934"repeated login failures from %s(%s)",935remotehost,936data_addr);937exit(0);938}939return;940}941}942if(!do_login(230, passwd))943return;944945/* Forget all about it... */946end_login();947}948949void950retrieve(const char *cmd, char *name)951{952FILE *fin = NULL, *dout;953struct stat st;954int (*closefunc) (FILE *);955char line[BUFSIZ];956957958if (cmd == 0) {959fin = fopen(name, "r");960closefunc = fclose;961st.st_size = 0;962if(fin == NULL){963int save_errno = errno;964struct cmds {965const char *ext;966const char *cmd;967const char *rev_cmd;968} cmds[] = {969{".tar", "/bin/gtar cPf - %s", NULL},970{".tar.gz", "/bin/gtar zcPf - %s", NULL},971{".tar.Z", "/bin/gtar ZcPf - %s", NULL},972{".gz", "/bin/gzip -c -- %s", "/bin/gzip -c -d -- %s"},973{".Z", "/bin/compress -c -- %s", "/bin/uncompress -c -- %s"},974{NULL, NULL}975};976struct cmds *p;977for(p = cmds; p->ext; p++){978char *tail = name + strlen(name) - strlen(p->ext);979char c = *tail;980981if(strcmp(tail, p->ext) == 0 &&982(*tail = 0) == 0 &&983access(name, R_OK) == 0){984snprintf (line, sizeof(line), p->cmd, name);985*tail = c;986break;987}988*tail = c;989if (p->rev_cmd != NULL) {990char *ext;991int ret;992993ret = asprintf(&ext, "%s%s", name, p->ext);994if (ret != -1) {995if (access(ext, R_OK) == 0) {996snprintf (line, sizeof(line),997p->rev_cmd, ext);998free(ext);999break;1000}1001free(ext);1002}1003}10041005}1006if(p->ext){1007fin = ftpd_popen(line, "r", 0, 0);1008closefunc = ftpd_pclose;1009st.st_size = -1;1010cmd = line;1011} else1012errno = save_errno;1013}1014} else {1015snprintf(line, sizeof(line), cmd, name);1016name = line;1017fin = ftpd_popen(line, "r", 1, 0);1018closefunc = ftpd_pclose;1019st.st_size = -1;1020}1021if (fin == NULL) {1022if (errno != 0) {1023perror_reply(550, name);1024if (cmd == 0) {1025LOGCMD("get", name);1026}1027}1028return;1029}1030byte_count = -1;1031if (cmd == 0){1032if(fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) {1033reply(550, "%s: not a plain file.", name);1034goto done;1035}1036}1037if (restart_point) {1038if (type == TYPE_A) {1039off_t i, n;1040int c;10411042n = restart_point;1043i = 0;1044while (i++ < n) {1045if ((c=getc(fin)) == EOF) {1046perror_reply(550, name);1047goto done;1048}1049if (c == '\n')1050i++;1051}1052} else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {1053perror_reply(550, name);1054goto done;1055}1056}1057dout = dataconn(name, st.st_size, "w");1058if (dout == NULL)1059goto done;1060set_buffer_size(fileno(dout), 0);1061send_data(fin, dout);1062fclose(dout);1063data = -1;1064pdata = -1;1065done:1066if (cmd == 0)1067LOGBYTES("get", name, byte_count);1068(*closefunc)(fin);1069}10701071/* filename sanity check */10721073int1074filename_check(char *filename)1075{1076char *p;10771078p = strrchr(filename, '/');1079if(p)1080filename = p + 1;10811082p = filename;10831084if(isalnum((unsigned char)*p)){1085p++;1086while(*p && (isalnum((unsigned char)*p) || strchr(good_chars, (unsigned char)*p)))1087p++;1088if(*p == '\0')1089return 0;1090}1091lreply(553, "\"%s\" is not an acceptable filename.", filename);1092lreply(553, "The filename must start with an alphanumeric "1093"character and must only");1094reply(553, "consist of alphanumeric characters or any of the following: %s",1095good_chars);1096return 1;1097}10981099void1100do_store(char *name, char *mode, int unique)1101{1102FILE *fout, *din;1103struct stat st;1104int (*closefunc) (FILE *);11051106if(guest && filename_check(name))1107return;1108if (unique) {1109char *uname;1110if (stat(name, &st) == 0) {1111if ((uname = gunique(name)) == NULL)1112return;1113name = uname;1114}1115LOGCMD(*mode == 'w' ? "put" : "append", name);1116}11171118if (restart_point)1119mode = "r+";1120fout = fopen(name, mode);1121closefunc = fclose;1122if (fout == NULL) {1123perror_reply(553, name);1124LOGCMD(*mode == 'w' ? "put" : "append", name);1125return;1126}1127byte_count = -1;1128if (restart_point) {1129if (type == TYPE_A) {1130off_t i, n;1131int c;11321133n = restart_point;1134i = 0;1135while (i++ < n) {1136if ((c=getc(fout)) == EOF) {1137perror_reply(550, name);1138goto done;1139}1140if (c == '\n')1141i++;1142}1143/*1144* We must do this seek to "current" position1145* because we are changing from reading to1146* writing.1147*/1148if (fseek(fout, 0L, SEEK_CUR) < 0) {1149perror_reply(550, name);1150goto done;1151}1152} else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {1153perror_reply(550, name);1154goto done;1155}1156}1157din = dataconn(name, (off_t)-1, "r");1158if (din == NULL)1159goto done;1160set_buffer_size(fileno(din), 1);1161if (receive_data(din, fout) == 0) {1162if((*closefunc)(fout) < 0)1163perror_reply(552, name);1164else {1165if (unique)1166reply(226, "Transfer complete (unique file name:%s).",1167name);1168else1169reply(226, "Transfer complete.");1170}1171} else1172(*closefunc)(fout);1173fclose(din);1174data = -1;1175pdata = -1;1176done:1177LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);1178}11791180static FILE *1181getdatasock(const char *mode, int domain)1182{1183int s, t, tries;11841185if (data >= 0)1186return (fdopen(data, mode));1187if (seteuid(0) < 0)1188fatal("Failed to seteuid");1189s = socket(domain, SOCK_STREAM, 0);1190if (s < 0)1191goto bad;1192socket_set_reuseaddr (s, 1);1193/* anchor socket to avoid multi-homing problems */1194socket_set_address_and_port (data_source,1195socket_get_address (ctrl_addr),1196socket_get_port (data_source));11971198for (tries = 1; ; tries++) {1199if (bind(s, data_source,1200socket_sockaddr_size (data_source)) >= 0)1201break;1202if (errno != EADDRINUSE || tries > 10)1203goto bad;1204sleep(tries);1205}1206if (seteuid(pw->pw_uid) < 0)1207fatal("Failed to seteuid");1208#ifdef IPTOS_THROUGHPUT1209socket_set_tos (s, IPTOS_THROUGHPUT);1210#endif1211return (fdopen(s, mode));1212bad:1213/* Return the real value of errno (close may change it) */1214t = errno;1215if (seteuid((uid_t)pw->pw_uid) < 0)1216fatal("Failed to seteuid");1217close(s);1218errno = t;1219return (NULL);1220}12211222static int1223accept_with_timeout(int socket,1224struct sockaddr *address,1225socklen_t *address_len,1226struct timeval *timeout)1227{1228int ret;1229fd_set rfd;1230FD_ZERO(&rfd);1231FD_SET(socket, &rfd);1232ret = select(socket + 1, &rfd, NULL, NULL, timeout);1233if(ret < 0)1234return ret;1235if(ret == 0) {1236errno = ETIMEDOUT;1237return -1;1238}1239return accept(socket, address, address_len);1240}12411242static FILE *1243dataconn(const char *name, off_t size, const char *mode)1244{1245char sizebuf[32];1246FILE *file;1247int domain, retry = 0;12481249file_size = size;1250byte_count = 0;1251if (size >= 0)1252snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", (long)size);1253else1254*sizebuf = '\0';1255if (pdata >= 0) {1256struct sockaddr_storage from_ss;1257struct sockaddr *from = (struct sockaddr *)&from_ss;1258struct timeval timeout;1259int s;1260socklen_t fromlen = sizeof(from_ss);12611262timeout.tv_sec = 15;1263timeout.tv_usec = 0;1264s = accept_with_timeout(pdata, from, &fromlen, &timeout);1265if (s < 0) {1266reply(425, "Can't open data connection.");1267close(pdata);1268pdata = -1;1269return (NULL);1270}1271close(pdata);1272pdata = s;1273#if defined(IPTOS_THROUGHPUT)1274if (from->sa_family == AF_INET)1275socket_set_tos(s, IPTOS_THROUGHPUT);1276#endif1277reply(150, "Opening %s mode data connection for '%s'%s.",1278type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);1279return (fdopen(pdata, mode));1280}1281if (data >= 0) {1282reply(125, "Using existing data connection for '%s'%s.",1283name, sizebuf);1284usedefault = 1;1285return (fdopen(data, mode));1286}1287if (usedefault)1288data_dest = his_addr;1289usedefault = 1;1290/*1291* Default to using the same socket type as the ctrl address,1292* unless we know the type of the data address.1293*/1294domain = data_dest->sa_family;1295if (domain == PF_UNSPEC)1296domain = ctrl_addr->sa_family;12971298file = getdatasock(mode, domain);1299if (file == NULL) {1300char data_addr[256];13011302if (inet_ntop (data_source->sa_family,1303socket_get_address(data_source),1304data_addr, sizeof(data_addr)) == NULL)1305strlcpy (data_addr, "unknown address",1306sizeof(data_addr));13071308reply(425, "Can't create data socket (%s,%d): %s.",1309data_addr,1310socket_get_port (data_source),1311strerror(errno));1312return (NULL);1313}1314data = fileno(file);1315while (connect(data, data_dest,1316socket_sockaddr_size(data_dest)) < 0) {1317if (errno == EADDRINUSE && retry < swaitmax) {1318sleep(swaitint);1319retry += swaitint;1320continue;1321}1322perror_reply(425, "Can't build data connection");1323fclose(file);1324data = -1;1325return (NULL);1326}1327reply(150, "Opening %s mode data connection for '%s'%s.",1328type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);1329return (file);1330}13311332/*1333* Tranfer the contents of "instr" to "outstr" peer using the appropriate1334* encapsulation of the data subject * to Mode, Structure, and Type.1335*1336* NB: Form isn't handled.1337*/1338static void1339send_data(FILE *instr, FILE *outstr)1340{1341int c, cnt, filefd, netfd;1342static char *buf;1343static size_t bufsize;13441345transflag = 1;1346switch (type) {13471348case TYPE_A:1349while ((c = getc(instr)) != EOF) {1350if (urgflag && handleoobcmd())1351return;1352byte_count++;1353if(c == '\n')1354sec_putc('\r', outstr);1355sec_putc(c, outstr);1356}1357sec_fflush(outstr);1358transflag = 0;1359urgflag = 0;1360if (ferror(instr))1361goto file_err;1362if (ferror(outstr))1363goto data_err;1364reply(226, "Transfer complete.");1365return;13661367case TYPE_I:1368case TYPE_L:1369#if 0 /* XXX handle urg flag */1370#if defined(HAVE_MMAP) && !defined(NO_MMAP)1371#ifndef MAP_FAILED1372#define MAP_FAILED (-1)1373#endif1374{1375struct stat st;1376char *chunk;1377int in = fileno(instr);1378if(fstat(in, &st) == 0 && S_ISREG(st.st_mode)1379&& st.st_size > 0) {1380/*1381* mmap zero bytes has potential of loosing, don't do it.1382*/1383chunk = mmap(0, st.st_size, PROT_READ,1384MAP_SHARED, in, 0);1385if((void *)chunk != (void *)MAP_FAILED) {1386cnt = st.st_size - restart_point;1387sec_write(fileno(outstr), chunk + restart_point, cnt);1388if (munmap(chunk, st.st_size) < 0)1389warn ("munmap");1390sec_fflush(outstr);1391byte_count = cnt;1392transflag = 0;1393urgflag = 0;1394}1395}1396}1397#endif1398#endif1399if(transflag) {1400struct stat st;14011402netfd = fileno(outstr);1403filefd = fileno(instr);1404buf = alloc_buffer (buf, &bufsize,1405fstat(filefd, &st) >= 0 ? &st : NULL);1406if (buf == NULL) {1407transflag = 0;1408urgflag = 0;1409perror_reply(451, "Local resource failure: malloc");1410return;1411}1412while ((cnt = read(filefd, buf, bufsize)) > 0 &&1413sec_write(netfd, buf, cnt) == cnt) {1414byte_count += cnt;1415if (urgflag && handleoobcmd())1416return;1417}1418sec_fflush(outstr); /* to end an encrypted stream */1419transflag = 0;1420urgflag = 0;1421if (cnt != 0) {1422if (cnt < 0)1423goto file_err;1424goto data_err;1425}1426}1427reply(226, "Transfer complete.");1428return;1429default:1430transflag = 0;1431urgflag = 0;1432reply(550, "Unimplemented TYPE %d in send_data", type);1433return;1434}14351436data_err:1437transflag = 0;1438urgflag = 0;1439perror_reply(426, "Data connection");1440return;14411442file_err:1443transflag = 0;1444urgflag = 0;1445perror_reply(551, "Error on input file");1446}14471448/*1449* Transfer data from peer to "outstr" using the appropriate encapulation of1450* the data subject to Mode, Structure, and Type.1451*1452* N.B.: Form isn't handled.1453*/1454static int1455receive_data(FILE *instr, FILE *outstr)1456{1457int cnt, bare_lfs = 0;1458static char *buf;1459static size_t bufsize;1460struct stat st;14611462transflag = 1;14631464buf = alloc_buffer (buf, &bufsize,1465fstat(fileno(outstr), &st) >= 0 ? &st : NULL);1466if (buf == NULL) {1467transflag = 0;1468urgflag = 0;1469perror_reply(451, "Local resource failure: malloc");1470return -1;1471}14721473switch (type) {14741475case TYPE_I:1476case TYPE_L:1477while ((cnt = sec_read(fileno(instr), buf, bufsize)) > 0) {1478if (write(fileno(outstr), buf, cnt) != cnt)1479goto file_err;1480byte_count += cnt;1481if (urgflag && handleoobcmd())1482return (-1);1483}1484if (cnt < 0)1485goto data_err;1486transflag = 0;1487urgflag = 0;1488return (0);14891490case TYPE_E:1491reply(553, "TYPE E not implemented.");1492transflag = 0;1493urgflag = 0;1494return (-1);14951496case TYPE_A:1497{1498char *p, *q;1499int cr_flag = 0;1500while ((cnt = sec_read(fileno(instr),1501buf + cr_flag,1502bufsize - cr_flag)) > 0){1503if (urgflag && handleoobcmd())1504return (-1);1505byte_count += cnt;1506cnt += cr_flag;1507cr_flag = 0;1508for(p = buf, q = buf; p < buf + cnt;) {1509if(*p == '\n')1510bare_lfs++;1511if(*p == '\r') {1512if(p == buf + cnt - 1){1513cr_flag = 1;1514p++;1515continue;1516}else if(p[1] == '\n'){1517*q++ = '\n';1518p += 2;1519continue;1520}1521}1522*q++ = *p++;1523}1524fwrite(buf, q - buf, 1, outstr);1525if(cr_flag)1526buf[0] = '\r';1527}1528if(cr_flag)1529putc('\r', outstr);1530fflush(outstr);1531if (ferror(instr))1532goto data_err;1533if (ferror(outstr))1534goto file_err;1535transflag = 0;1536urgflag = 0;1537if (bare_lfs) {1538lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n"1539" File may not have transferred correctly.\r\n",1540bare_lfs);1541}1542return (0);1543}1544default:1545reply(550, "Unimplemented TYPE %d in receive_data", type);1546transflag = 0;1547urgflag = 0;1548return (-1);1549}15501551data_err:1552transflag = 0;1553urgflag = 0;1554perror_reply(426, "Data Connection");1555return (-1);15561557file_err:1558transflag = 0;1559urgflag = 0;1560perror_reply(452, "Error writing file");1561return (-1);1562}15631564void1565statfilecmd(char *filename)1566{1567FILE *fin;1568int c;1569char line[LINE_MAX];15701571snprintf(line, sizeof(line), "/bin/ls -la -- %s", filename);1572fin = ftpd_popen(line, "r", 1, 0);1573lreply(211, "status of %s:", filename);1574while ((c = getc(fin)) != EOF) {1575if (c == '\n') {1576if (ferror(stdout)){1577perror_reply(421, "control connection");1578ftpd_pclose(fin);1579dologout(1);1580/* NOTREACHED */1581}1582if (ferror(fin)) {1583perror_reply(551, filename);1584ftpd_pclose(fin);1585return;1586}1587putc('\r', stdout);1588}1589putc(c, stdout);1590}1591ftpd_pclose(fin);1592reply(211, "End of Status");1593}15941595void1596statcmd(void)1597{1598#if 01599struct sockaddr_in *sin;1600u_char *a, *p;16011602lreply(211, "%s FTP server (%s) status:", hostname, version);1603printf(" %s\r\n", version);1604printf(" Connected to %s", remotehost);1605if (!isdigit((unsigned char)remotehost[0]))1606printf(" (%s)", inet_ntoa(his_addr.sin_addr));1607printf("\r\n");1608if (logged_in) {1609if (guest)1610printf(" Logged in anonymously\r\n");1611else1612printf(" Logged in as %s\r\n", pw->pw_name);1613} else if (askpasswd)1614printf(" Waiting for password\r\n");1615else1616printf(" Waiting for user name\r\n");1617printf(" TYPE: %s", typenames[type]);1618if (type == TYPE_A || type == TYPE_E)1619printf(", FORM: %s", formnames[form]);1620if (type == TYPE_L)1621#if NBBY == 81622printf(" %d", NBBY);1623#else1624printf(" %d", bytesize); /* need definition! */1625#endif1626printf("; STRUcture: %s; transfer MODE: %s\r\n",1627strunames[stru], modenames[mode]);1628if (data != -1)1629printf(" Data connection open\r\n");1630else if (pdata != -1) {1631printf(" in Passive mode");1632sin = &pasv_addr;1633goto printaddr;1634} else if (usedefault == 0) {1635printf(" PORT");1636sin = &data_dest;1637printaddr:1638a = (u_char *) &sin->sin_addr;1639p = (u_char *) &sin->sin_port;1640#define UC(b) (((int) b) & 0xff)1641printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),1642UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));1643#undef UC1644} else1645printf(" No data connection\r\n");1646#endif1647reply(211, "End of status");1648}16491650void1651fatal(char *s)1652{16531654reply(451, "Error in server: %s\n", s);1655reply(221, "Closing connection due to server error.");1656dologout(0);1657/* NOTREACHED */1658}16591660static void1661int_reply(int, char *, const char *, va_list)1662#ifdef __GNUC__1663__attribute__ ((format (printf, 3, 0)))1664#endif1665;16661667static void1668int_reply(int n, char *c, const char *fmt, va_list ap)1669{1670char buf[10240];1671char *p;1672p=buf;1673if(n){1674snprintf(p, sizeof(buf), "%d%s", n, c);1675p+=strlen(p);1676}1677vsnprintf(p, sizeof(buf) - strlen(p), fmt, ap);1678p+=strlen(p);1679snprintf(p, sizeof(buf) - strlen(p), "\r\n");1680p+=strlen(p);1681sec_fprintf(stdout, "%s", buf);1682fflush(stdout);1683if (debug)1684syslog(LOG_DEBUG, "<--- %s- ", buf);1685}16861687void1688reply(int n, const char *fmt, ...)1689{1690va_list ap;1691va_start(ap, fmt);1692int_reply(n, " ", fmt, ap);1693delete_ftp_command();1694va_end(ap);1695}16961697void1698lreply(int n, const char *fmt, ...)1699{1700va_list ap;1701va_start(ap, fmt);1702int_reply(n, "-", fmt, ap);1703va_end(ap);1704}17051706void1707nreply(const char *fmt, ...)1708{1709va_list ap;1710va_start(ap, fmt);1711int_reply(0, NULL, fmt, ap);1712va_end(ap);1713}17141715static void1716ack(char *s)1717{17181719reply(250, "%s command successful.", s);1720}17211722void1723nack(char *s)1724{17251726reply(502, "%s command not implemented.", s);1727}17281729void1730do_delete(char *name)1731{1732struct stat st;17331734LOGCMD("delete", name);1735if (stat(name, &st) < 0) {1736perror_reply(550, name);1737return;1738}1739if (S_ISDIR(st.st_mode)) {1740if (rmdir(name) < 0) {1741perror_reply(550, name);1742return;1743}1744goto done;1745}1746if (unlink(name) < 0) {1747perror_reply(550, name);1748return;1749}1750done:1751ack("DELE");1752}17531754void1755cwd(const char *path)1756{17571758if (chdir(path) < 0)1759perror_reply(550, path);1760else1761ack("CWD");1762}17631764void1765makedir(char *name)1766{17671768LOGCMD("mkdir", name);1769if(guest && filename_check(name))1770return;1771if (mkdir(name, 0777) < 0)1772perror_reply(550, name);1773else{1774if(guest)1775chmod(name, 0700); /* guest has umask 777 */1776reply(257, "MKD command successful.");1777}1778}17791780void1781removedir(char *name)1782{17831784LOGCMD("rmdir", name);1785if (rmdir(name) < 0)1786perror_reply(550, name);1787else1788ack("RMD");1789}17901791void1792pwd(void)1793{1794char path[MaxPathLen];1795char *ret;17961797/* SunOS has a broken getcwd that does popen(pwd) (!!!), this1798* failes miserably when running chroot1799*/1800ret = getcwd(path, sizeof(path));1801if (ret == NULL)1802reply(550, "%s.", strerror(errno));1803else1804reply(257, "\"%s\" is current directory.", path);1805}18061807char *1808renamefrom(char *name)1809{1810struct stat st;18111812if (stat(name, &st) < 0) {1813perror_reply(550, name);1814return NULL;1815}1816reply(350, "File exists, ready for destination name");1817return (name);1818}18191820void1821renamecmd(char *from, char *to)1822{18231824LOGCMD2("rename", from, to);1825if(guest && filename_check(to))1826return;1827if (rename(from, to) < 0)1828perror_reply(550, "rename");1829else1830ack("RNTO");1831}18321833static void1834dolog(struct sockaddr *sa, int len)1835{1836getnameinfo_verified (sa, len, remotehost, sizeof(remotehost),1837NULL, 0, 0);1838#ifdef HAVE_SETPROCTITLE1839snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);1840setproctitle("%s", proctitle);1841#endif /* HAVE_SETPROCTITLE */18421843if (logging) {1844char data_addr[256];18451846if (inet_ntop (his_addr->sa_family,1847socket_get_address(his_addr),1848data_addr, sizeof(data_addr)) == NULL)1849strlcpy (data_addr, "unknown address",1850sizeof(data_addr));185118521853syslog(LOG_INFO, "connection from %s(%s)",1854remotehost,1855data_addr);1856}1857}18581859/*1860* Record logout in wtmp file1861* and exit with supplied status.1862*/1863void1864dologout(int status)1865{1866transflag = 0;1867urgflag = 0;1868if (logged_in) {1869#if KRB51870cond_kdestroy();1871#endif1872seteuid((uid_t)0); /* No need to check, we call exit() below */1873ftpd_logwtmp(ttyline, "", "");1874}1875/* beware of flushing buffers after a SIGPIPE */1876#ifdef XXX1877exit(status);1878#else1879_exit(status);1880#endif1881}18821883void abor(void)1884{1885if (!transflag)1886return;1887reply(426, "Transfer aborted. Data connection closed.");1888reply(226, "Abort successful");1889transflag = 0;1890}18911892static void1893myoob(int signo)1894{1895urgflag = 1;1896}18971898static char *1899mec_space(char *p)1900{1901while(isspace(*(unsigned char *)p))1902p++;1903return p;1904}19051906static int1907handleoobcmd(void)1908{1909char *cp;19101911/* only process if transfer occurring */1912if (!transflag)1913return 0;19141915urgflag = 0;19161917cp = tmpline;1918if (ftpd_getline(cp, sizeof(tmpline)) == NULL) {1919reply(221, "You could at least say goodbye.");1920dologout(0);1921}19221923if (strncasecmp("MIC", cp, 3) == 0) {1924mec(mec_space(cp + 3), prot_safe);1925} else if (strncasecmp("CONF", cp, 4) == 0) {1926mec(mec_space(cp + 4), prot_confidential);1927} else if (strncasecmp("ENC", cp, 3) == 0) {1928mec(mec_space(cp + 3), prot_private);1929} else if (!allow_insecure_oob) {1930reply(533, "Command protection level denied "1931"for paranoid reasons.");1932goto out;1933}19341935if (secure_command())1936cp = ftp_command;19371938if (strcasecmp(cp, "ABOR\r\n") == 0) {1939abor();1940} else if (strcasecmp(cp, "STAT\r\n") == 0) {1941if (file_size != (off_t) -1)1942reply(213, "Status: %ld of %ld bytes transferred",1943(long)byte_count,1944(long)file_size);1945else1946reply(213, "Status: %ld bytes transferred",1947(long)byte_count);1948}1949out:1950return (transflag == 0);1951}19521953/*1954* Note: a response of 425 is not mentioned as a possible response to1955* the PASV command in RFC959. However, it has been blessed as1956* a legitimate response by Jon Postel in a telephone conversation1957* with Rick Adams on 25 Jan 89.1958*/1959void1960pasv(void)1961{1962socklen_t len;1963char *p, *a;1964struct sockaddr_in *sin;19651966if (ctrl_addr->sa_family != AF_INET) {1967reply(425,1968"You cannot do PASV with something that's not IPv4");1969return;1970}19711972if(pdata != -1)1973close(pdata);19741975pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);1976if (pdata < 0) {1977perror_reply(425, "Can't open passive connection");1978return;1979}1980pasv_addr->sa_family = ctrl_addr->sa_family;1981socket_set_address_and_port (pasv_addr,1982socket_get_address (ctrl_addr),19830);1984socket_set_portrange(pdata, restricted_data_ports,1985pasv_addr->sa_family);1986if (seteuid(0) < 0)1987fatal("Failed to seteuid");1988if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {1989if (seteuid(pw->pw_uid) < 0)1990fatal("Failed to seteuid");1991goto pasv_error;1992}1993if (seteuid(pw->pw_uid) < 0)1994fatal("Failed to seteuid");1995len = sizeof(pasv_addr_ss);1996if (getsockname(pdata, pasv_addr, &len) < 0)1997goto pasv_error;1998if (listen(pdata, 1) < 0)1999goto pasv_error;2000sin = (struct sockaddr_in *)pasv_addr;2001a = (char *) &sin->sin_addr;2002p = (char *) &sin->sin_port;20032004#define UC(b) (((int) b) & 0xff)20052006reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),2007UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));2008return;20092010pasv_error:2011close(pdata);2012pdata = -1;2013perror_reply(425, "Can't open passive connection");2014return;2015}20162017void2018epsv(char *proto)2019{2020socklen_t len;20212022pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);2023if (pdata < 0) {2024perror_reply(425, "Can't open passive connection");2025return;2026}2027pasv_addr->sa_family = ctrl_addr->sa_family;2028socket_set_address_and_port (pasv_addr,2029socket_get_address (ctrl_addr),20300);2031socket_set_portrange(pdata, restricted_data_ports,2032pasv_addr->sa_family);2033if (seteuid(0) < 0)2034fatal("Failed to seteuid");2035if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {2036if (seteuid(pw->pw_uid))2037fatal("Failed to seteuid");2038goto pasv_error;2039}2040if (seteuid(pw->pw_uid) < 0)2041fatal("Failed to seteuid");2042len = sizeof(pasv_addr_ss);2043if (getsockname(pdata, pasv_addr, &len) < 0)2044goto pasv_error;2045if (listen(pdata, 1) < 0)2046goto pasv_error;20472048reply(229, "Entering Extended Passive Mode (|||%d|)",2049ntohs(socket_get_port (pasv_addr)));2050return;20512052pasv_error:2053close(pdata);2054pdata = -1;2055perror_reply(425, "Can't open passive connection");2056return;2057}20582059void2060eprt(char *str)2061{2062char *end;2063char sep;2064int af;2065int ret;2066int port;20672068usedefault = 0;2069if (pdata >= 0) {2070close(pdata);2071pdata = -1;2072}20732074sep = *str++;2075if (sep == '\0') {2076reply(500, "Bad syntax in EPRT");2077return;2078}2079af = strtol (str, &end, 0);2080if (af == 0 || *end != sep) {2081reply(500, "Bad syntax in EPRT");2082return;2083}2084str = end + 1;2085switch (af) {2086#ifdef HAVE_IPV62087case 2 :2088data_dest->sa_family = AF_INET6;2089break;2090#endif2091case 1 :2092data_dest->sa_family = AF_INET;2093break;2094default :2095reply(522, "Network protocol %d not supported, use (1"2096#ifdef HAVE_IPV62097",2"2098#endif2099")", af);2100return;2101}2102end = strchr (str, sep);2103if (end == NULL) {2104reply(500, "Bad syntax in EPRT");2105return;2106}2107*end = '\0';2108ret = inet_pton (data_dest->sa_family, str,2109socket_get_address (data_dest));21102111if (ret != 1) {2112reply(500, "Bad address syntax in EPRT");2113return;2114}2115str = end + 1;2116port = strtol (str, &end, 0);2117if (port == 0 || *end != sep) {2118reply(500, "Bad port syntax in EPRT");2119return;2120}2121if (port < IPPORT_RESERVED) {2122reply(500, "Bad port in invalid range in EPRT");2123return;2124}2125socket_set_port (data_dest, htons(port));21262127if (paranoid &&2128(data_dest->sa_family != his_addr->sa_family ||2129memcmp(socket_get_address(data_dest), socket_get_address(his_addr), socket_sockaddr_size(data_dest)) != 0))2130{2131reply(500, "Bad address in EPRT");2132}2133reply(200, "EPRT command successful.");2134}21352136/*2137* Generate unique name for file with basename "local".2138* The file named "local" is already known to exist.2139* Generates failure reply on error.2140*/2141static char *2142gunique(char *local)2143{2144static char new[MaxPathLen];2145struct stat st;2146int count;2147char *cp;21482149cp = strrchr(local, '/');2150if (cp)2151*cp = '\0';2152if (stat(cp ? local : ".", &st) < 0) {2153perror_reply(553, cp ? local : ".");2154return NULL;2155}2156if (cp)2157*cp = '/';2158for (count = 1; count < 100; count++) {2159snprintf (new, sizeof(new), "%s.%d", local, count);2160if (stat(new, &st) < 0)2161return (new);2162}2163reply(452, "Unique file name cannot be created.");2164return (NULL);2165}21662167/*2168* Format and send reply containing system error number.2169*/2170void2171perror_reply(int code, const char *string)2172{2173reply(code, "%s: %s.", string, strerror(errno));2174}21752176static char *onefile[] = {2177"",217802179};21802181void2182list_file(char *file)2183{2184if(use_builtin_ls) {2185FILE *dout;2186dout = dataconn(file, -1, "w");2187if (dout == NULL)2188return;2189set_buffer_size(fileno(dout), 0);2190if(builtin_ls(dout, file) == 0)2191reply(226, "Transfer complete.");2192else2193reply(451, "Requested action aborted. Local error in processing.");2194fclose(dout);2195data = -1;2196pdata = -1;2197} else {2198#ifdef HAVE_LS_A2199const char *cmd = "/bin/ls -lA %s";2200#else2201const char *cmd = "/bin/ls -la %s";2202#endif2203retrieve(cmd, file);2204}2205}22062207void2208send_file_list(char *whichf)2209{2210struct stat st;2211DIR *dirp = NULL;2212struct dirent *dir;2213FILE *dout = NULL;2214char **dirlist, *dirname;2215int simple = 0;2216int freeglob = 0;2217glob_t gl;2218char buf[MaxPathLen];22192220if (strpbrk(whichf, "~{[*?") != NULL) {2221int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|2222#ifdef GLOB_MAXPATH2223GLOB_MAXPATH2224#else2225GLOB_LIMIT2226#endif2227;22282229memset(&gl, 0, sizeof(gl));2230freeglob = 1;2231if (glob(whichf, flags, 0, &gl)) {2232reply(550, "not found");2233goto out;2234} else if (gl.gl_pathc == 0) {2235errno = ENOENT;2236perror_reply(550, whichf);2237goto out;2238}2239dirlist = gl.gl_pathv;2240} else {2241onefile[0] = whichf;2242dirlist = onefile;2243simple = 1;2244}22452246while ((dirname = *dirlist++)) {22472248if (urgflag && handleoobcmd())2249goto out;22502251if (stat(dirname, &st) < 0) {2252/*2253* If user typed "ls -l", etc, and the client2254* used NLST, do what the user meant.2255*/2256if (dirname[0] == '-' && *dirlist == NULL &&2257transflag == 0) {2258list_file(dirname);2259goto out;2260}2261perror_reply(550, whichf);2262goto out;2263}22642265if (S_ISREG(st.st_mode)) {2266if (dout == NULL) {2267dout = dataconn("file list", (off_t)-1, "w");2268if (dout == NULL)2269goto out;2270transflag = 1;2271}2272snprintf(buf, sizeof(buf), "%s%s\n", dirname,2273type == TYPE_A ? "\r" : "");2274sec_write(fileno(dout), buf, strlen(buf));2275byte_count += strlen(dirname) + 1;2276continue;2277} else if (!S_ISDIR(st.st_mode))2278continue;22792280if ((dirp = opendir(dirname)) == NULL)2281continue;22822283while ((dir = readdir(dirp)) != NULL) {2284char nbuf[MaxPathLen];22852286if (urgflag && handleoobcmd())2287goto out;22882289if (!strcmp(dir->d_name, "."))2290continue;2291if (!strcmp(dir->d_name, ".."))2292continue;22932294snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);22952296/*2297* We have to do a stat to insure it's2298* not a directory or special file.2299*/2300if (simple || (stat(nbuf, &st) == 0 &&2301S_ISREG(st.st_mode))) {2302if (dout == NULL) {2303dout = dataconn("file list", (off_t)-1, "w");2304if (dout == NULL)2305goto out;2306transflag = 1;2307}2308if(strncmp(nbuf, "./", 2) == 0)2309snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2,2310type == TYPE_A ? "\r" : "");2311else2312snprintf(buf, sizeof(buf), "%s%s\n", nbuf,2313type == TYPE_A ? "\r" : "");2314sec_write(fileno(dout), buf, strlen(buf));2315byte_count += strlen(nbuf) + 1;2316}2317}2318closedir(dirp);2319}2320if (dout == NULL)2321reply(550, "No files found.");2322else if (ferror(dout) != 0)2323perror_reply(550, "Data connection");2324else2325reply(226, "Transfer complete.");23262327out:2328transflag = 0;2329if (dout != NULL){2330sec_write(fileno(dout), buf, 0); /* XXX flush */23312332fclose(dout);2333}2334data = -1;2335pdata = -1;2336if (freeglob)2337globfree(&gl);2338}233923402341int2342find(char *pattern)2343{2344char line[1024];2345FILE *f;23462347snprintf(line, sizeof(line),2348"/bin/locate -d %s -- %s",2349ftp_rooted("/etc/locatedb"),2350pattern);2351f = ftpd_popen(line, "r", 1, 1);2352if(f == NULL){2353perror_reply(550, "/bin/locate");2354return 1;2355}2356lreply(200, "Output from find.");2357while(fgets(line, sizeof(line), f)){2358if(line[strlen(line)-1] == '\n')2359line[strlen(line)-1] = 0;2360nreply("%s", line);2361}2362reply(200, "Done");2363ftpd_pclose(f);2364return 0;2365}2366236723682369