summaryrefslogtreecommitdiffstats
path: root/src/appl/gssftp/ftpd/ftpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/appl/gssftp/ftpd/ftpd.c')
-rw-r--r--src/appl/gssftp/ftpd/ftpd.c2725
1 files changed, 0 insertions, 2725 deletions
diff --git a/src/appl/gssftp/ftpd/ftpd.c b/src/appl/gssftp/ftpd/ftpd.c
deleted file mode 100644
index 7958facfeb..0000000000
--- a/src/appl/gssftp/ftpd/ftpd.c
+++ /dev/null
@@ -1,2725 +0,0 @@
-/*
- * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
- All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)ftpd.c 5.40 (Berkeley) 7/2/91";
-#endif /* not lint */
-
-/*
- * FTP server.
- */
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/file.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-
-#define FTP_NAMES
-#include <arpa/ftp.h>
-#include <arpa/inet.h>
-#include <arpa/telnet.h>
-
-#include <signal.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <time.h>
-#include <pwd.h>
-#ifdef HAVE_SHADOW
-#include <shadow.h>
-#endif
-#include <grp.h>
-#include <setjmp.h>
-#ifndef POSIX_SETJMP
-#undef sigjmp_buf
-#undef sigsetjmp
-#undef siglongjmp
-#define sigjmp_buf jmp_buf
-#define sigsetjmp(j,s) setjmp(j)
-#define siglongjmp longjmp
-#endif
-#include <netdb.h>
-#include <errno.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef STDARG
-#if (defined(__STDC__) && ! defined(VARARGS)) || defined(HAVE_STDARG_H)
-#define STDARG
-#endif
-#endif
-#ifdef STDARG
-#include <stdarg.h>
-#endif
-#include "pathnames.h"
-#include <libpty.h>
-
-#include <k5-platform.h>
-
-#ifdef NEED_SETENV
-extern int setenv(char *, char *, int);
-#endif
-
-#ifndef L_SET
-#define L_SET 0
-#endif
-#ifndef L_INCR
-#define L_INCR 1
-#endif
-
-#ifndef HAVE_STRERROR
-#define strerror(error) (sys_errlist[error])
-#ifdef NEED_SYS_ERRLIST
-extern char *sys_errlist[];
-#endif
-#endif
-
-extern char *mktemp ();
-char *ftpusers;
-extern int yyparse(void);
-
-#include <k5-util.h>
-#include "port-sockets.h"
-
-#ifdef GSSAPI
-#include <gssapi/gssapi.h>
-#include <gssapi/gssapi_generic.h>
-#include <gssapi/gssapi_krb5.h>
-gss_ctx_id_t gcontext;
-gss_buffer_desc client_name;
-static char *gss_services[] = { "ftp", "host", NULL };
-
-#include <krb5.h>
-krb5_context kcontext;
-krb5_ccache ccache;
-
-static void ftpd_gss_convert_creds(char *name, gss_cred_id_t);
-static int ftpd_gss_userok(gss_buffer_t, char *name);
-
-static void log_gss_error(int, OM_uint32, OM_uint32, const char *);
-
-#endif /* GSSAPI */
-
-char *auth_type; /* Authentication succeeded? If so, what type? */
-static char *temp_auth_type;
-int authorized; /* Auth succeeded and was accepted by gssapi */
-int have_creds; /* User has credentials on disk */
-
-/*
- * File containing login names
- * NOT to be used on this machine.
- * Commonly used to disallow uucp.
- */
-#include "ftpd_var.h"
-#include "secure.h"
-
-extern char *crypt();
-extern char version[];
-extern char *home; /* pointer to home directory for glob */
-extern FILE *ftpd_popen(), *fopen(), *freopen();
-extern int ftpd_pclose(), fclose();
-extern char cbuf[];
-extern off_t restart_point;
-
-struct sockaddr_in ctrl_addr;
-struct sockaddr_in data_source;
-struct sockaddr_in data_dest;
-struct sockaddr_in his_addr;
-struct sockaddr_in pasv_addr;
-
-int data;
-jmp_buf errcatch;
-sigjmp_buf urgcatch;
-int logged_in;
-struct passwd *pw;
-int debug;
-int allow_ccc = 0; /* whether or not the CCC command is allowed */
-int ccc_ok = 0; /* whether or not to accept cleartext commands */
-int timeout = 900; /* timeout after 15 minutes of inactivity */
-int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
-int logging;
-int authlevel;
-int want_creds;
-int guest;
-int restricted;
-int type;
-int clevel; /* control protection level */
-int dlevel; /* data protection level */
-int form;
-int stru; /* avoid C keyword */
-int mode;
-int usedefault = 1; /* for data transfers */
-int pdata = -1; /* for passive mode */
-int transflag;
-off_t file_size;
-off_t byte_count;
-#if !defined(CMASK) || CMASK == 0
-#undef CMASK
-#define CMASK 027
-#endif
-int defumask = CMASK; /* default umask value */
-char tmpline[FTP_BUFSIZ];
-char pathbuf[MAXPATHLEN + 1];
-char hostname[MAXHOSTNAMELEN];
-char remotehost[MAXHOSTNAMELEN];
-char rhost_addra[16];
-char *rhost_sane;
-
-/* Defines for authlevel */
-#define AUTHLEVEL_NONE 0
-#define AUTHLEVEL_AUTHENTICATE 1
-#define AUTHLEVEL_AUTHORIZE 2
-
-/*
- * Timeout intervals for retrying connections
- * to hosts that don't accept PORT cmds. This
- * is a kludge, but given the problems with TCP...
- */
-#define SWAITMAX 90 /* wait at most 90 seconds */
-#define SWAITINT 5 /* interval between retries */
-
-int swaitmax = SWAITMAX;
-int swaitint = SWAITINT;
-
-void lostconn(int), myoob(int);
-FILE *getdatasock(char *);
-#if defined(__STDC__)
-/*
- * The following prototypes must be ANSI for systems for which
- * sizeof(off_t) > sizeof(int) to prevent stack overflow problems
- */
-FILE *dataconn(char *name, off_t size, char *mymode);
-void send_data(FILE *instr, FILE *outstr, off_t blksize);
-#else
-void send_data();
-FILE *dataconn();
-#endif
-static void dolog(struct sockaddr_in *);
-static int receive_data(FILE *, FILE *);
-static void login(char *passwd, int logincode);
-static void end_login(void);
-static int disallowed_user(char *);
-static int restricted_user(char *);
-static int checkuser(char *);
-static char *gunique(char *);
-
-#ifdef SETPROCTITLE
-char **Argv = NULL; /* pointer to argument vector */
-char *LastArgv = NULL; /* end of argv */
-char proctitle[FTP_BUFSIZ]; /* initial part of title */
-#endif /* SETPROCTITLE */
-
-#ifdef __SCO__
-/* sco has getgroups and setgroups but no initgroups */
-int initgroups(char* name, gid_t basegid) {
- gid_t others[NGROUPS_MAX+1];
- int ngrps;
-
- others[0] = basegid;
- ngrps = getgroups(NGROUPS_MAX, others+1);
- return setgroups(ngrps+1, others);
-}
-#endif
-
-int stripdomain = 1;
-int maxhostlen = 0;
-int always_ip = 0;
-
-int
-main(argc, argv, envp)
- int argc;
- char *argv[];
- char **envp;
-{
- int addrlen, c, on = 1, tos, port = -1;
- extern char *optarg;
- extern int optopt;
- char *option_string = "AaCcdElp:T:t:U:u:vw:";
- ftpusers = _PATH_FTPUSERS_DEFAULT;
-
- debug = 0;
-#ifdef SETPROCTITLE
- /*
- * Save start and extent of argv for setproctitle.
- */
- Argv = argv;
- while (*envp)
- envp++;
- LastArgv = envp[-1] + strlen(envp[-1]);
-#endif /* SETPROCTITLE */
-
-#ifdef GSSAPI
- krb5_init_context(&kcontext);
-#endif
-
- while ((c = getopt(argc, argv, option_string)) != -1) {
- switch (c) {
-
- case 'v':
- debug = 1;
- break;
-
- case 'd':
- debug = 1;
- break;
-
- case 'E':
- if (!authlevel)
- authlevel = AUTHLEVEL_AUTHENTICATE;
- break;
-
- case 'l':
- logging ++;
- break;
-
- case 'a':
- authlevel = AUTHLEVEL_AUTHORIZE;
- break;
-
- case 'A':
- authlevel = AUTHLEVEL_AUTHENTICATE;
- break;
-
- case 'C':
- want_creds = 1;
- break;
-
- case 'c':
- allow_ccc = 1;
- break;
-
- case 'p':
- port = atoi(optarg);
- break;
-
- case 't':
- timeout = atoi(optarg);
- if (maxtimeout < timeout)
- maxtimeout = timeout;
- break;
-
- case 'T':
- maxtimeout = atoi(optarg);
- if (timeout > maxtimeout)
- timeout = maxtimeout;
- break;
-
- case 'u':
- {
- int val = 0;
- char *umask_val = optarg;
-
- while (*umask_val >= '0' && *umask_val <= '9') {
- val = val*8 + *umask_val - '0';
- umask_val++;
- }
- if (*umask_val != ' ' && *umask_val != '\0')
- fprintf(stderr, "ftpd: Bad value for -u\n");
- else
- defumask = val;
- break;
- }
-
- case 'U':
- ftpusers = optarg;
- break;
-
- case 'w':
- {
- char *foptarg;
- foptarg = optarg;
-
- if (!strcmp(foptarg, "ip"))
- always_ip = 1;
- else {
- char *cp2;
- cp2 = strchr(foptarg, ',');
- if (cp2 == NULL)
- maxhostlen = atoi(foptarg);
- else if (*(++cp2)) {
- if (!strcmp(cp2, "striplocal"))
- stripdomain = 1;
- else if (!strcmp(cp2, "nostriplocal"))
- stripdomain = 0;
- else {
- fprintf(stderr,
- "ftpd: bad arg to -w\n");
- exit(1);
- }
- *(--cp2) = '\0';
- maxhostlen = atoi(foptarg);
- }
- }
- break;
- }
- default:
- fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
- (char)optopt);
- break;
- }
- }
-
- if (port != -1) {
- struct sockaddr_in sin4;
- int s, ns;
- socklen_t sz;
-
- /* Accept an incoming connection on port. */
- sin4.sin_family = AF_INET;
- sin4.sin_addr.s_addr = INADDR_ANY;
- sin4.sin_port = htons(port);
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0) {
- perror("socket");
- exit(1);
- }
- (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
- (char *)&on, sizeof(on));
- if (bind(s, (struct sockaddr *)&sin4, sizeof sin4) < 0) {
- perror("bind");
- exit(1);
- }
- if (listen(s, 1) < 0) {
- perror("listen");
- exit(1);
- }
- sz = sizeof sin4;
- ns = accept(s, (struct sockaddr *)&sin4, &sz);
- if (ns < 0) {
- perror("accept");
- exit(1);
- }
- (void) close(s);
- (void) dup2(ns, 0);
- (void) dup2(ns, 1);
- (void) dup2(ns, 2);
- if (ns > 2)
- (void) close(ns);
- }
-
- /*
- * LOG_NDELAY sets up the logging connection immediately,
- * necessary for anonymous ftp's that chroot and can't do it later.
- */
-#ifndef LOG_NDELAY
-/* Ultrix syslog does not support NDELAY. */
-#define LOG_NDELAY 0
-#endif
-#ifndef LOG_DAEMON
-#define LOG_DAEMON 0
-#endif
- openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
-
- addrlen = sizeof (his_addr);
- if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
- syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
- exit(1);
- }
- addrlen = sizeof (ctrl_addr);
- if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
- syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
- exit(1);
- }
-#ifdef IP_TOS
-#ifdef IPTOS_LOWDELAY
- tos = IPTOS_LOWDELAY;
- if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
-#endif
-#endif
- port = ntohs(ctrl_addr.sin_port);
- data_source.sin_port = htons(port - 1);
-
- (void) freopen("/dev/null", "w", stderr);
- (void) signal(SIGPIPE, lostconn);
- (void) signal(SIGCHLD, SIG_IGN);
-#ifdef SIGURG
-#ifdef POSIX_SIGNALS
- {
- struct sigaction sa;
-
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sa.sa_handler = myoob;
- if (sigaction(SIGURG, &sa, NULL) < 0)
- syslog(LOG_ERR, "signal: %m");
- }
-#else
- if ((long)signal(SIGURG, myoob) < 0)
- syslog(LOG_ERR, "signal: %m");
-#endif /* POSIX_SIGNALS */
-#endif /* SIGURG */
-
- /* Try to handle urgent data inline */
-#ifdef SO_OOBINLINE
- if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
- syslog(LOG_ERR, "setsockopt: %m");
-#endif
-
-#ifdef F_SETOWN
- if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
- syslog(LOG_ERR, "fcntl F_SETOWN: %m");
-#endif
- dolog(&his_addr);
- /*
- * Set up default state
- */
- data = -1;
- clevel = dlevel = PROT_C;
- type = TYPE_A;
- form = FORM_N;
- stru = STRU_F;
- mode = MODE_S;
- tmpline[0] = '\0';
- (void) gethostname(hostname, sizeof (hostname));
- reply(220, "%s FTP server (%s) ready.", hostname, version);
- (void) setjmp(errcatch);
- for (;;)
- (void) yyparse();
- /* NOTREACHED */
-}
-
-void
-lostconn(sig)
-int sig;
-{
- if (debug)
- syslog(LOG_DEBUG, "lost connection");
- dologout(-1);
-}
-
-static char ttyline[20];
-
-/*
- * Helper function for sgetpwnam().
- */
-static char *
-sgetsave(s)
- char *s;
-{
- char *new = strdup(s);
-
- if (new == NULL) {
- perror_reply(421, "Local resource failure: malloc");
- dologout(1);
- /* NOTREACHED */
- }
- return (new);
-}
-
-/*
- * Save the result of a getpwnam. Used for USER command, since
- * the data returned must not be clobbered by any other command
- * (e.g., globbing).
- */
-static struct passwd *
-sgetpwnam(name)
- char *name;
-{
- static struct passwd save;
- register struct passwd *p;
-#ifdef HAVE_SHADOW
- register struct spwd *sp;
-#endif
- if ((p = getpwnam(name)) == NULL)
- return (p);
- if (save.pw_name) {
- free(save.pw_name);
- free(save.pw_passwd);
- free(save.pw_gecos);
- free(save.pw_dir);
- free(save.pw_shell);
- }
- save = *p;
- save.pw_name = sgetsave(p->pw_name);
-#ifdef HAVE_SHADOW
- if ((sp = getspnam(name)) == NULL)
- save.pw_passwd = sgetsave(p->pw_passwd);
- else
- save.pw_passwd = sgetsave(sp->sp_pwdp);
-#else
- save.pw_passwd = sgetsave(p->pw_passwd);
-#endif
- save.pw_gecos = sgetsave(p->pw_gecos);
- save.pw_dir = sgetsave(p->pw_dir);
- save.pw_shell = sgetsave(p->pw_shell);
- return (&save);
-}
-
-/*
- * Expand the given pathname relative to the current working directory.
- */
-static char *
-path_expand(path)
- char *path;
-{
- pathbuf[0] = '\x0';
- if (!path) return pathbuf;
- /* Don't bother with getcwd() if the path is absolute */
- if (path[0] != '/') {
- if (!getcwd(pathbuf, sizeof pathbuf)) {
- pathbuf[0] = '\x0';
- syslog(LOG_ERR, "getcwd() failed");
- }
- else {
- int len = strlen(pathbuf);
- if (pathbuf[len-1] != '/') {
- pathbuf[len++] = '/';
- pathbuf[len] = '\x0';
- }
- }
- }
- return strncat(pathbuf, path,
- sizeof (pathbuf) - strlen(pathbuf) - 1);
-}
-
-/*
- * Set data channel protection level
- */
-void
-setdlevel(prot_level)
-int prot_level;
-{
- switch (prot_level) {
- case PROT_S:
-#ifndef NOENCRYPTION
- case PROT_P:
-#endif
- if (auth_type)
- case PROT_C:
- reply(200, "Data channel protection level set to %s.",
- (dlevel = prot_level) == PROT_S ?
- "safe" : dlevel == PROT_P ?
- "private" : "clear");
- else
- default: reply(536, "%s protection level not supported.",
- levelnames[prot_level]);
- }
-}
-
-int login_attempts; /* number of failed login attempts */
-int askpasswd; /* had user command, ask for passwd */
-
-/*
- * USER command.
- * Sets global passwd pointer pw if named account exists and is acceptable;
- * sets askpasswd if a PASS command is expected. If logged in previously,
- * need to reset state. If name is "ftp" or "anonymous", the name is not in
- * ftpusers, and ftp account exists, set guest and pw, then just return.
- * If account doesn't exist, ask for passwd anyway. Otherwise, check user
- * requesting login privileges. Disallow anyone who does not have a standard
- * shell as returned by getusershell(). Disallow anyone mentioned in the file
- * ftpusers to allow people such as root and uucp to be avoided, except
- * for users whose names are followed by whitespace and then the keyword
- * "restrict." Restricted users are allowed to login, but a chroot() is
- * done to their home directory.
- */
-void
-user(name)
- char *name;
-{
- register char *cp;
- char *shell;
- char buf[FTP_BUFSIZ];
-#ifdef HAVE_GETUSERSHELL
- char *getusershell();
-#endif
-
- if (logged_in) {
- if (guest) {
- reply(530, "Can't change user from guest login.");
- return;
- }
- end_login();
- }
-
- authorized = guest = 0;
- if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
- if (disallowed_user("ftp") || disallowed_user("anonymous"))
- reply(530, "User %s access denied.", name);
- else if ((pw = sgetpwnam("ftp")) != NULL) {
- guest = 1;
- askpasswd = 1;
- reply(331, "Guest login ok, send ident as password.");
- } else
- reply(530, "User %s unknown.", name);
- return;
- }
-
- /*
- * If authentication is required, check that before anything
- * else to avoid leaking information.
- */
- if (authlevel && !auth_type) {
- reply(530,
- "Must perform authentication before identifying USER.");
- return;
- }
-
- pw = sgetpwnam(name);
- if (pw) {
- if ((shell = pw->pw_shell) == NULL || *shell == 0)
- shell = "/bin/sh";
-#ifdef HAVE_GETUSERSHELL
- setusershell();
- while ((cp = getusershell()) != NULL)
- if (strcmp(cp, shell) == 0)
- break;
- endusershell();
-#else
- cp = shell;
-#endif
- if (cp == NULL || disallowed_user(name)) {
- reply(530, "User %s access denied.", name);
- if (logging)
- syslog(LOG_NOTICE,
- "FTP LOGIN REFUSED FROM %s, %s (%s)",
- rhost_addra, remotehost, name);
- pw = (struct passwd *) NULL;
- return;
- }
- restricted = restricted_user(name);
- }
-
- if (auth_type) {
- int result;
-#ifdef GSSAPI
- if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
- int len;
-
- authorized = ftpd_gss_userok(&client_name, name) == 0;
- len = sizeof("GSSAPI user is not authorized as "
- "; Password required.")
- + strlen(client_name.value)
- + strlen(name);
- if (len >= sizeof(buf)) {
- syslog(LOG_ERR, "user: username too long");
- name = "[username too long]";
- }
- snprintf(buf, sizeof(buf),
- "GSSAPI user %s is%s authorized as %s",
- (char *) client_name.value,
- authorized ? "" : " not",
- name);
- }
-#endif /* GSSAPI */
-
- if (!authorized && authlevel == AUTHLEVEL_AUTHORIZE) {
- strncat(buf, "; Access denied.",
- sizeof(buf) - strlen(buf) - 1);
- result = 530;
- pw = NULL;
- } else if (!authorized || (want_creds && !have_creds)) {
- strncat(buf, "; Password required.",
- sizeof(buf) - strlen(buf) - 1);
- askpasswd = 1;
- result = 331;
- } else
- result = 232;
- if (result == 232)
- login(NULL, result);
- reply(result, "%s", buf);
- syslog(authorized ? LOG_INFO : LOG_ERR, "%s", buf);
- return;
- }
-
- /* User didn't authenticate and authentication wasn't required. */
- reply(331, "Password required for %s.", name);
- askpasswd = 1;
-
- /*
- * Delay before reading passwd after first failed
- * attempt to slow down passwd-guessing programs.
- */
- if (login_attempts)
- sleep((unsigned) login_attempts);
-}
-
-/*
- * Check if a user is in the file ftpusers.
- * Return 1 if they are (a disallowed user), -1 if their username
- * is followed by "restrict." (a restricted user). Otherwise return 0.
- */
-static int
-checkuser(name)
- char *name;
-{
- register FILE *fd;
- register char *p;
- char line[FTP_BUFSIZ];
-
- if ((fd = fopen(ftpusers, "r")) != NULL) {
- while (fgets(line, sizeof(line), fd) != NULL) {
- if ((p = strchr(line, '\n')) != NULL) {
- *p = '\0';
- if (line[0] == '#')
- continue;
- if (strcmp(line, name) == 0)
- return (1);
- if (strncmp(line, name, strlen(name)) == 0) {
- int i = strlen(name) + 1;
-
- /* Make sure foo doesn't match foobar */
- if (line[i] == '\0' || !isspace((int) line[i]))
- continue;
- /* Ignore whitespace */
- while (isspace((int) line[++i]));
-
- if (strcmp(&line[i], "restrict") == 0)
- return (-1);
- else
- return (1);
- }
- }
- }
- (void) fclose(fd);
- }
-
- return (0);
-}
-
-static int
-disallowed_user(name)
- char *name;
-{
- return(checkuser(name) == 1);
-}
-
-static int
-restricted_user(name)
- char *name;
-{
- return(checkuser(name) == -1);
-}
-
-/*
- * Terminate login as previous user, if any, resetting state;
- * used when USER command is given or login fails.
- */
-static void
-end_login()
-{
-
- (void) krb5_seteuid((uid_t)0);
- if (logged_in)
- pty_logwtmp(ttyline, "", "");
- if (have_creds) {
-#ifdef GSSAPI
- krb5_cc_destroy(kcontext, ccache);
-#endif
- have_creds = 0;
- }
- pw = NULL;
- logged_in = 0;
- guest = 0;
-}
-
-static int
-kpass(name, passwd)
-char *name, *passwd;
-{
-#ifdef GSSAPI
- krb5_principal server, me;
- krb5_creds my_creds;
- krb5_timestamp now;
-#endif /* GSSAPI */
- char ccname[MAXPATHLEN];
-
-#ifdef GSSAPI
- memset(&my_creds, 0, sizeof(my_creds));
- if (krb5_parse_name(kcontext, name, &me))
- return 0;
- my_creds.client = me;
-
- snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_ftpd%ld",
- (long) getpid());
- if (krb5_cc_resolve(kcontext, ccname, &ccache))
- return(0);
- if (krb5_cc_initialize(kcontext, ccache, me))
- return(0);
- if (krb5_build_principal_ext(kcontext, &server,
- krb5_princ_realm(kcontext, me)->length,
- krb5_princ_realm(kcontext, me)->data,
- KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
- krb5_princ_realm(kcontext, me)->length,
- krb5_princ_realm(kcontext, me)->data,
- 0))
- goto nuke_ccache;
-
- my_creds.server = server;
- if (krb5_timeofday(kcontext, &now))
- goto nuke_ccache;
- my_creds.times.starttime = 0; /* start timer when
- request gets to KDC */
- my_creds.times.endtime = now + 60 * 60 * 10;
- my_creds.times.renew_till = 0;
-
- if (krb5_get_init_creds_password(kcontext, &my_creds, me,
- passwd, NULL, NULL, 0, NULL, NULL))
- goto nuke_ccache;
-
- if (krb5_cc_store_cred(kcontext, ccache, &my_creds))
- goto nuke_ccache;
-
- if (!want_creds) {
- krb5_cc_destroy(kcontext, ccache);
- return(1);
- }
-
- have_creds = 1;
- return(1);
-#endif /* GSSAPI */
-
-nuke_ccache:
-#ifdef GSSAPI
- krb5_cc_destroy(kcontext, ccache);
-#endif /* GSSAPI */
- return(0);
-}
-
-void
-pass(passwd)
- char *passwd;
-{
- char *xpasswd, *salt;
-
- if (authorized && !want_creds) {
- reply(202, "PASS command superfluous.");
- return;
- }
-
- if (logged_in || askpasswd == 0) {
- reply(503, "Login with USER first.");
- return;
- }
-
- if (!guest) {
- /* "ftp" is only account allowed no password */
- if (pw == NULL)
- salt = "xx";
- else
- salt = pw->pw_passwd;
-#ifdef __SCO__
- /* SCO does not provide crypt. */
- xpasswd = "";
-#else
- xpasswd = crypt(passwd, salt);
-#endif
- /* Fail if:
- * pw is NULL
- * kpass fails and we want_creds
- * kpass fails and the user has no local password
- * kpass fails and the provided password doesn't match pw
- */
- if (pw == NULL || (!kpass(pw->pw_name, passwd) &&
- (want_creds || !*pw->pw_passwd ||
- strcmp(xpasswd, pw->pw_passwd)))) {
- pw = NULL;
- sleep(5);
- if (++login_attempts >= 3) {
- reply(421,
- "Login incorrect, closing connection.");
- syslog(LOG_NOTICE,
- "repeated login failures from %s (%s)",
- rhost_addra, remotehost);
- dologout(0);
- }
- reply(530, "Login incorrect.");
- return;
- }
- }
- login_attempts = 0; /* this time successful */
-
- login(passwd, 0);
- return;
-}
-
-static void
-login(passwd, logincode)
- char *passwd;
- int logincode;
-{
- if (have_creds) {
-#ifdef GSSAPI
- const char *ccname = krb5_cc_get_name(kcontext, ccache);
- chown(ccname, pw->pw_uid, pw->pw_gid);
-#endif
- }
-
- (void) krb5_setegid((gid_t)pw->pw_gid);
- (void) initgroups(pw->pw_name, pw->pw_gid);
-
- /* open wtmp before chroot */
- (void) snprintf(ttyline, sizeof(ttyline), "ftp%ld", (long) getpid());
- pty_logwtmp(ttyline, pw->pw_name, rhost_sane);
- logged_in = 1;
-
- if (guest || restricted) {
- if (chroot(pw->pw_dir) < 0) {
- reply(550, "Can't set privileges.");
- goto bad;
- }
- }
-#ifdef HAVE_SETLUID
- /*
- * If we're on a system which keeps track of login uids, then
- * set the login uid. If this fails this opens up a problem on DEC OSF
- * with C2 enabled.
- */
- if (((uid_t)getluid() != pw->pw_uid)
- && setluid((uid_t)pw->pw_uid) < 0) {
- reply(550, "Can't set luid.");
- goto bad;
- }
-#endif
- if (krb5_seteuid((uid_t)pw->pw_uid) < 0) {
- reply(550, "Can't set uid.");
- goto bad;
- }
- if (guest) {
- /*
- * We MUST do a chdir() after the chroot. Otherwise
- * the old current directory will be accessible as "."
- * outside the new root!
- */
- if (chdir("/") < 0) {
- reply(550, "Can't set guest privileges.");
- goto bad;
- }
- } else {
- if (chdir(restricted ? "/" : pw->pw_dir) < 0) {
- if (chdir("/") < 0) {
- reply(530, "User %s: can't change directory to %s.",
- pw->pw_name, pw->pw_dir);
- goto bad;
- } else {
- if (!logincode)
- logincode = 230;
- lreply(logincode, "No directory! Logging in with home=/");
- }
- }
- }
- if (guest) {
- reply(230, "Guest login ok, access restrictions apply.");
-#ifdef SETPROCTITLE
- snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%.*s",
- rhost_sane, passwd);
- setproctitle(proctitle);
-#endif /* SETPROCTITLE */
- if (logging)
- syslog(LOG_INFO,
- "ANONYMOUS FTP LOGIN FROM %s, %s (%s)",
- rhost_addra, remotehost, passwd);
- } else {
- if (askpasswd) {
- askpasswd = 0;
- reply(230, "User %s logged in.", pw->pw_name);
- }
-#ifdef SETPROCTITLE
- snprintf(proctitle, sizeof(proctitle), "%s: %s",
- rhost_sane, pw->pw_name);
- setproctitle(proctitle);
-#endif /* SETPROCTITLE */
- if (logging)
- syslog(LOG_INFO, "FTP LOGIN FROM %s, %s (%s)",
- rhost_addra, remotehost, pw->pw_name);
- }
- home = pw->pw_dir; /* home dir for globbing */
- (void) umask(defumask);
- return;
-bad:
- /* Forget all about it... */
- end_login();
-}
-
-void
-retrieve(cmd, name)
- char *cmd, *name;
-{
- FILE *fin, *dout;
- struct stat st;
- int (*closefunc)();
-
- if (logging > 1 && !cmd)
- syslog(LOG_NOTICE, "get %s", path_expand(name));
- if (cmd == 0) {
- fin = fopen(name, "r"), closefunc = fclose;
- st.st_size = 0;
- } else {
- char line[FTP_BUFSIZ];
-
- if (strlen(cmd) + strlen(name) + 1 >= sizeof(line)) {
- syslog(LOG_ERR, "retrieve: filename too long");
- reply(501, "filename too long");
- return;
- }
- (void) snprintf(line, sizeof(line), cmd, name), name = line;
- fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
- st.st_size = -1;
-#ifndef NOSTBLKSIZE
- st.st_blksize = FTP_BUFSIZ;
-#endif
- }
- if (fin == NULL) {
- if (errno != 0)
- perror_reply(550, name);
- return;
- }
- if (cmd == 0 &&
- (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
- reply(550, "%s: not a plain file.", name);
- goto done;
- }
- if (restart_point) {
- if (type == TYPE_A) {
- register int i, n, c;
-
- n = restart_point;
- i = 0;
- while (i++ < n) {
- if ((c=getc(fin)) == EOF) {
- perror_reply(550, name);
- goto done;
- }
- if (c == '\n')
- i++;
- }
- } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
- perror_reply(550, name);
- goto done;
- }
- }
- dout = dataconn(name, st.st_size, "w");
- if (dout == NULL)
- goto done;
-#ifndef NOSTBLKSIZE
- send_data(fin, dout, st.st_blksize);
-#else
- send_data(fin, dout, FTP_BUFSIZ);
-#endif
- (void) fclose(dout);
- data = -1;
- pdata = -1;
-done:
- (*closefunc)(fin);
- if (logging > 2 && !cmd)
- syslog(LOG_NOTICE, "get: %i bytes transferred", byte_count);
-}
-
-void
-store_file(name, fmode, unique)
- char *name, *fmode;
- int unique;
-{
- FILE *fout, *din;
- struct stat st;
- int (*closefunc)();
-
- if (logging > 1) syslog(LOG_NOTICE, "put %s", path_expand(name));
-
- if (unique && stat(name, &st) == 0 &&
- (name = gunique(name)) == NULL)
- return;
-
- if (restart_point)
- fmode = "r+w";
- fout = fopen(name, fmode);
- closefunc = fclose;
- if (fout == NULL) {
- perror_reply(553, name);
- return;
- }
- if (restart_point) {
- if (type == TYPE_A) {
- register int i, n, c;
-
- n = restart_point;
- i = 0;
- while (i++ < n) {
- if ((c=getc(fout)) == EOF) {
- perror_reply(550, name);
- goto done;
- }
- if (c == '\n')
- i++;
- }
- /*
- * We must do this seek to "current" position
- * because we are changing from reading to
- * writing.
- */
- if (fseek(fout, 0L, L_INCR) < 0) {
- perror_reply(550, name);
- goto done;
- }
- } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
- perror_reply(550, name);
- goto done;
- }
- }
- din = dataconn(name, (off_t)-1, "r");
- if (din == NULL)
- goto done;
- if (receive_data(din, fout) == 0) {
- if (unique)
- reply(226, "Transfer complete (unique file name:%s).",
- name);
- else
- reply(226, "Transfer complete.");
- }
- (void) fclose(din);
- data = -1;
- pdata = -1;
-done:
- (*closefunc)(fout);
- if (logging > 2)
- syslog(LOG_NOTICE, "put: %i bytes transferred", byte_count);
-}
-
-FILE *
-getdatasock(fmode)
- char *fmode;
-{
- int s, on = 1, tries;
-
- if (data >= 0)
- return (fdopen(data, fmode));
- (void) krb5_seteuid((uid_t)0);
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0)
- goto bad;
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
- (char *) &on, sizeof (on)) < 0)
- goto bad;
- /* anchor socket to avoid multi-homing problems */
- data_source.sin_family = AF_INET;
- data_source.sin_addr = ctrl_addr.sin_addr;
- for (tries = 1; ; tries++) {
- if (bind(s, (struct sockaddr *)&data_source,
- sizeof (data_source)) >= 0)
- break;
- if (errno != EADDRINUSE || tries > 10)
- goto bad;
- sleep(tries);
- }
- if (krb5_seteuid((uid_t)pw->pw_uid)) {
- fatal("seteuid user");
- }
-#ifdef IP_TOS
-#ifdef IPTOS_THROUGHPUT
- on = IPTOS_THROUGHPUT;
- if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
-#endif
-#endif
- return (fdopen(s, fmode));
-bad:
- if (krb5_seteuid((uid_t)pw->pw_uid)) {
- fatal("seteuid user");
- }
- (void) close(s);
- return (NULL);
-}
-
-FILE *
-dataconn(name, size, fmode)
- char *name;
- off_t size;
- char *fmode;
-{
- char sizebuf[32];
- FILE *file;
- int retry = 0, tos;
-
- file_size = size;
- byte_count = 0;
- if (size != (off_t) -1)
- /* cast size to long in case sizeof(off_t) > sizeof(long) */
- (void) snprintf (sizebuf, sizeof(sizebuf), " (%ld bytes)",
- (long)size);
- else
- sizebuf[0] = '\0';
- if (pdata >= 0) {
- int s, fromlen = sizeof(data_dest);
-
- s = accept(pdata, (struct sockaddr *)&data_dest, &fromlen);
- if (s < 0) {
- reply(425, "Can't open data connection.");
- (void) close(pdata);
- pdata = -1;
- return(NULL);
- }
- (void) close(pdata);
- pdata = s;
-#ifdef IP_TOS
-#ifdef IPTOS_LOWDELAY
- tos = IPTOS_LOWDELAY;
- (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
- sizeof(int));
-#endif
-#endif
- reply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- return(fdopen(pdata, fmode));
- }
- if (data >= 0) {
- reply(125, "Using existing data connection for %s%s.",
- name, sizebuf);
- usedefault = 1;
- return (fdopen(data, fmode));
- }
- if (usedefault)
- data_dest = his_addr;
- usedefault = 1;
- file = getdatasock(fmode);
- if (file == NULL) {
- reply(425, "Can't create data socket (%s,%d): %s.",
- inet_ntoa(data_source.sin_addr),
- ntohs(data_source.sin_port), strerror(errno));
- return (NULL);
- }
- data = fileno(file);
- while (connect(data, (struct sockaddr *)&data_dest,
- sizeof (data_dest)) < 0) {
- if (errno == EADDRINUSE && retry < swaitmax) {
- sleep((unsigned) swaitint);
- retry += swaitint;
- continue;
- }
- perror_reply(425, "Can't build data connection");
- (void) fclose(file);
- data = -1;
- return (NULL);
- }
- reply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- return (file);
-}
-
-/*
- * XXX callers need to limit total length of output string to
- * FTP_BUFSIZ
- */
-void
-secure_error(char *fmt, ...)
-{
- char buf[FTP_BUFSIZ];
- va_list ap;
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- reply(535, "%s", buf);
- syslog(LOG_ERR, "%s", buf);
-}
-
-/*
- * Tranfer the contents of "instr" to
- * "outstr" peer using the appropriate
- * encapsulation of the data subject
- * to Mode, Structure, and Type.
- *
- * NB: Form isn't handled.
- */
-void send_data(instr, outstr, blksize)
- FILE *instr, *outstr;
- off_t blksize;
-{
- register int c, cnt;
- register char *buf;
- int netfd, filefd;
- volatile int ret = 0;
-
- transflag++;
- if (sigsetjmp(urgcatch, 1)) {
- transflag = 0;
- (void)secure_flush(fileno(outstr));
- return;
- }
- switch (type) {
-
- case TYPE_A:
- while ((c = getc(instr)) != EOF) {
- byte_count++;
- if (c == '\n') {
- if (ferror(outstr) ||
- (ret = secure_putc('\r', outstr)) < 0)
- goto data_err;
- }
- if ((ret = secure_putc(c, outstr)) < 0)
- goto data_err;
- }
- transflag = 0;
- if (ferror(instr))
- goto file_err;
- if (ferror(outstr) ||
- (ret = secure_flush(fileno(outstr))) < 0)
- goto data_err;
- reply(226, "Transfer complete.");
- return;
-
- case TYPE_I:
- case TYPE_L:
- if ((buf = malloc((u_int)blksize)) == NULL) {
- transflag = 0;
- perror_reply(451, "Local resource failure: malloc");
- return;
- }
- netfd = fileno(outstr);
- filefd = fileno(instr);
- while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
- (ret = secure_write(netfd, buf, cnt)) == cnt)
- byte_count += cnt;
- transflag = 0;
- (void)free(buf);
- if (cnt != 0) {
- if (cnt < 0)
- goto file_err;
- goto data_err;
- }
- if ((ret = secure_flush(netfd)) < 0)
- goto data_err;
- reply(226, "Transfer complete.");
- return;
- default:
- transflag = 0;
- reply(550, "Unimplemented TYPE %d in send_data", type);
- return;
- }
-
-data_err:
- transflag = 0;
- if (ret != -2) perror_reply(426, "Data connection");
- return;
-
-file_err:
- transflag = 0;
- perror_reply(551, "Error on input file");
-}
-
-/*
- * Transfer data from peer to
- * "outstr" using the appropriate
- * encapulation of the data subject
- * to Mode, Structure, and Type.
- *
- * N.B.: Form isn't handled.
- */
-static int
-receive_data(instr, outstr)
- FILE *instr, *outstr;
-{
- register int c;
- volatile int cnt, bare_lfs = 0;
- char buf[FTP_BUFSIZ];
- int ret = 0;
-
- transflag++;
- if (sigsetjmp(urgcatch, 1)) {
- transflag = 0;
- return (-1);
- }
- switch (type) {
-
- case TYPE_I:
- case TYPE_L:
- while ((cnt = secure_read(fileno(instr), buf, sizeof buf)) > 0) {
- if (write(fileno(outstr), buf, cnt) != cnt)
- goto file_err;
- byte_count += cnt;
- }
- transflag = 0;
- ret = cnt;
- if (cnt < 0)
- goto data_err;
- return (0);
-
- case TYPE_E:
- reply(553, "TYPE E not implemented.");
- transflag = 0;
- return (-1);
-
- case TYPE_A:
- while ((c = secure_getc(instr)) >= 0) {
- byte_count++;
- if (c == '\n')
- bare_lfs++;
- while (c == '\r') {
- if (ferror(outstr))
- goto data_err;
- if ((c = secure_getc(instr)) != '\n') {
- (void) putc ('\r', outstr);
- if (c == '\0')
- goto contin2;
- }
- }
- if (c < 0) break;
- (void) putc(c, outstr);
- contin2: ;
- }
- fflush(outstr);
- ret = c;
- if (c == -2 || ferror(instr))
- goto data_err;
- if (ferror(outstr))
- goto file_err;
- transflag = 0;
- if (bare_lfs) {
- lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
- reply(0, " File may not have transferred correctly.");
- }
- return (0);
- default:
- reply(550, "Unimplemented TYPE %d in receive_data", type);
- transflag = 0;
- return (-1);
- }
-
-data_err:
- transflag = 0;
- if (ret != -2) perror_reply(426, "Data Connection");
- return (-1);
-
-file_err:
- transflag = 0;
- perror_reply(452, "Error writing file");
- return (-1);
-}
-
-void
-statfilecmd(filename)
- char *filename;
-{
- char line[FTP_BUFSIZ];
- FILE *fin;
- int c, n;
- char str[FTP_BUFSIZ], *p;
-
- if (strlen(filename) + sizeof("/bin/ls -lgA ")
- >= sizeof(line)) {
- reply(501, "filename too long");
- return;
- }
- (void) snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
- fin = ftpd_popen(line, "r");
- lreply(211, "status of %s:", filename);
- p = str;
- n = 0;
- while ((c = getc(fin)) != EOF) {
- if (c == '\n') {
- if (ferror(stdout)){
- perror_reply(421, "control connection");
- (void) ftpd_pclose(fin);
- dologout(1);
- /* NOTREACHED */
- }
- if (ferror(fin)) {
- perror_reply(551, filename);
- (void) ftpd_pclose(fin);
- return;
- }
- *p = '\0';
- reply(0, "%s", str);
- p = str;
- n = 0;
- } else {
- *p++ = c;
- n++;
- if (n >= sizeof(str)) {
- reply(551, "output line too long");
- (void) ftpd_pclose(fin);
- return;
- }
- }
- }
- if (p != str) {
- *p = '\0';
- reply(0, "%s", str);
- }
- (void) ftpd_pclose(fin);
- reply(211, "End of Status");
-}
-
-void
-statcmd()
-{
- struct sockaddr_in *sin4;
- u_char *a, *p;
- char str[FTP_BUFSIZ];
-
- lreply(211, "%s FTP server status:", hostname);
- reply(0, " %s", version);
- snprintf(str, sizeof(str), " Connected to %s (%s)",
- remotehost[0] ? remotehost : "", rhost_addra);
- reply(0, "%s", str);
- if (auth_type) reply(0, " Authentication type: %s", auth_type);
- if (logged_in) {
- if (guest)
- reply(0, " Logged in anonymously");
- else
- reply(0, " Logged in as %s", pw->pw_name);
- } else if (askpasswd)
- reply(0, " Waiting for password");
- else if (temp_auth_type)
- reply(0, " Waiting for authentication data");
- else
- reply(0, " Waiting for user name");
- reply(0, " Protection level: %s", levelnames[dlevel]);
- snprintf(str, sizeof(str), " TYPE: %s", typenames[type]);
- if (type == TYPE_A || type == TYPE_E) {
- snprintf(&str[strlen(str)], sizeof(str) - strlen(str),
- ", FORM: %s", formnames[form]);
- }
- if (type == TYPE_L)
- strncat(str, " 8", sizeof (str) - strlen(str) - 1);
- snprintf(&str[strlen(str)], sizeof(str) - strlen(str),
- "; STRUcture: %s; transfer MODE: %s",
- strunames[stru], modenames[mode]);
- reply(0, "%s", str);
- if (data != -1)
- strlcpy(str, " Data connection open", sizeof(str));
- else if (pdata != -1) {
- strlcpy(str, " in Passive mode", sizeof(str));
- sin4 = &pasv_addr;
- goto printaddr;
- } else if (usedefault == 0) {
- sin4 = &data_dest;
-printaddr:
- a = (u_char *) &sin4->sin_addr;
- p = (u_char *) &sin4->sin_port;
-#define UC(b) (((int) b) & 0xff)
- snprintf(str, sizeof(str), " PORT (%d,%d,%d,%d,%d,%d)",
- UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]),
- UC(p[1]));
-#undef UC
- } else
- strlcpy(str, " No data connection", sizeof(str));
- reply(0, "%s", str);
- reply(211, "End of status");
-}
-
-void
-fatal(s)
- char *s;
-{
- reply(451, "Error in server: %s", s);
- reply(221, "Closing connection due to server error.");
- dologout(0);
- /* NOTREACHED */
-}
-
-char cont_char = ' ';
-
-/*
- * XXX callers need to limit total length of output string to
- * FTP_BUFSIZ bytes for now.
- */
-#ifdef STDARG
-void
-reply(int n, char *fmt, ...)
-#else
-/* VARARGS2 */
-void
-reply(n, fmt, p0, p1, p2, p3, p4, p5)
- int n;
- char *fmt;
-#endif
-{
- char buf[FTP_BUFSIZ];
-#ifdef STDARG
- va_list ap;
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-#else
- snprintf(buf, sizeof(buf), fmt, p0, p1, p2, p3, p4, p5);
-#endif
-
- if (auth_type) {
- /*
- * Deal with expansion in mk_{safe,priv},
- * radix_encode, gss_seal, plus slop.
- */
- char in[FTP_BUFSIZ*3/2], out[FTP_BUFSIZ*3/2];
- int length = 0, kerror;
- if (n) snprintf(in, sizeof(in), "%d%c", n, cont_char);
- else in[0] = '\0';
- strncat(in, buf, sizeof (in) - strlen(in) - 1);
-#ifdef GSSAPI
- /* reply (based on level) */
- if (strcmp(auth_type, "GSSAPI") == 0) {
- gss_buffer_desc in_buf, out_buf;
- OM_uint32 maj_stat, min_stat;
- int conf_state;
-
- in_buf.value = in;
- in_buf.length = strlen(in);
- maj_stat = gss_seal(&min_stat, gcontext,
- clevel == PROT_P, /* private */
- GSS_C_QOP_DEFAULT,
- &in_buf, &conf_state,
- &out_buf);
- if (maj_stat != GSS_S_COMPLETE) {
-#if 0
-/* Don't setup an infinite loop */
- /* generally need to deal */
- secure_gss_error(maj_stat, min_stat,
- (clevel==PROT_P)?
- "gss_seal ENC didn't complete":
- "gss_seal MIC didn't complete");
-#endif /* 0 */
- } else if ((clevel == PROT_P) && !conf_state) {
-#if 0
-/* Don't setup an infinite loop */
- secure_error("GSSAPI didn't encrypt message");
-#endif /* 0 */
- } else {
- memcpy(out, out_buf.value,
- length=out_buf.length);
- gss_release_buffer(&min_stat, &out_buf);
- }
- }
-#endif /* GSSAPI */
- /* Other auth types go here ... */
- if (length >= sizeof(in) / 4 * 3) {
- syslog(LOG_ERR, "input to radix_encode too long");
- fputs(in, stdout);
- } else if ((kerror = radix_encode(out, in, &length, 0))) {
- syslog(LOG_ERR, "Couldn't encode reply (%s)",
- radix_error(kerror));
- fputs(in,stdout);
- } else
- printf("%s%c%s", clevel == PROT_P ? "632" : "631",
- n ? cont_char : '-', in);
- } else {
- if (n) printf("%d%c", n, cont_char);
- fputs(buf, stdout);
- }
- printf("\r\n");
- (void)fflush(stdout);
- if (debug) {
- if (n) syslog(LOG_DEBUG, "<--- %d%c", n, cont_char);
- syslog(LOG_DEBUG, "%s", buf);
- }
-}
-
-/*
- * XXX callers need to limit total length of output string to
- * FTP_BUFSIZ
- */
-#ifdef STDARG
-void
-lreply(int n, char *fmt, ...)
-#else
-/* VARARGS2 */
-void
-lreply(n, fmt, p0, p1, p2, p3, p4, p5)
- int n;
- char *fmt;
-#endif
-{
- char buf[FTP_BUFSIZ];
-#ifdef STDARG
- va_list ap;
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-#else
- snprintf(buf, sizeof(buf), fmt, p0, p1, p2, p3, p4, p5);
-#endif
- cont_char = '-';
- reply(n, "%s", buf);
- cont_char = ' ';
-}
-
-void
-ack(s)
- char *s;
-{
- reply(250, "%s command successful.", s);
-}
-
-void
-nack(s)
- char *s;
-{
- reply(502, "%s command not implemented.", s);
-}
-
-/* ARGSUSED */
-void
-yyerror(s)
- char *s;
-{
- char *cp;
-
- cp = strchr(cbuf,'\n');
- if (cp)
- *cp = '\0';
- reply(500, "'%.*s': command not understood.",
- (int) (FTP_BUFSIZ - sizeof("'': command not understood.")),
- cbuf);
-}
-
-void
-delete_file(name)
- char *name;
-{
- struct stat st;
-
- if (logging > 1) syslog(LOG_NOTICE, "del %s", path_expand(name));
-
- if (stat(name, &st) < 0) {
- perror_reply(550, name);
- return;
- }
- if ((st.st_mode&S_IFMT) == S_IFDIR) {
- if (rmdir(name) < 0) {
- perror_reply(550, name);
- return;
- }
- goto done;
- }
- if (unlink(name) < 0) {
- perror_reply(550, name);
- return;
- }
-done:
- ack("DELE");
-}
-
-void
-cwd(path)
- char *path;
-{
- if (chdir(path) < 0)
- perror_reply(550, path);
- else
- ack("CWD");
-}
-
-void
-makedir(name)
- char *name;
-{
- if (logging > 1) syslog(LOG_NOTICE, "mkdir %s", path_expand(name));
-
- if (mkdir(name, 0777) < 0)
- perror_reply(550, name);
- else
- reply(257, "MKD command successful.");
-}
-
-void
-removedir(name)
- char *name;
-{
- if (logging > 1) syslog(LOG_NOTICE, "rmdir %s", path_expand(name));
-
- if (rmdir(name) < 0)
- perror_reply(550, name);
- else
- ack("RMD");
-}
-
-void
-pwd()
-{
- if (getcwd(pathbuf, sizeof pathbuf) == (char *)NULL)
-#ifdef POSIX
- perror_reply(550, pathbuf);
-#else
- reply(550, "%s.", pathbuf);
-#endif
- else
- reply(257, "\"%s\" is current directory.", pathbuf);
-}
-
-char *
-renamefrom(name)
- char *name;
-{
- struct stat st;
-
- if (stat(name, &st) < 0) {
- perror_reply(550, name);
- return ((char *)0);
- }
- reply(350, "File exists, ready for destination name");
- return (name);
-}
-
-void
-renamecmd(from, to)
- char *from, *to;
-{
- if(logging > 1)
- syslog(LOG_NOTICE, "rename %s %s", path_expand(from), to);
-
- if (rename(from, to) < 0)
- perror_reply(550, "rename");
- else
- ack("RNTO");
-}
-
-static void
-dolog(sin4)
- struct sockaddr_in *sin4;
-{
- struct hostent *hp = gethostbyaddr((char *)&sin4->sin_addr,
- sizeof (struct in_addr), AF_INET);
- time_t t, time();
- extern char *ctime();
- krb5_error_code retval;
-
- if (hp != NULL) {
- (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
- remotehost[sizeof (remotehost) - 1] = '\0';
- } else
- remotehost[0] = '\0';
- strncpy(rhost_addra, inet_ntoa(sin4->sin_addr), sizeof (rhost_addra));
- rhost_addra[sizeof (rhost_addra) - 1] = '\0';
- retval = pty_make_sane_hostname((struct sockaddr *) sin4, maxhostlen,
- stripdomain, always_ip, &rhost_sane);
- if (retval) {
- fprintf(stderr, "make_sane_hostname: %s\n",
- error_message(retval));
- exit(1);
- }
-#ifdef SETPROCTITLE
- snprintf(proctitle, sizeof(proctitle), "%s: connected", rhost_sane);
- setproctitle(proctitle);
-#endif /* SETPROCTITLE */
-
- if (logging) {
- t = time((time_t *) 0);
- syslog(LOG_INFO, "connection from %s (%s) at %s",
- rhost_addra, remotehost, ctime(&t));
- }
-}
-
-/*
- * Record logout in wtmp file
- * and exit with supplied status.
- */
-void
-dologout(status)
- int status;
-{
- if (logged_in) {
- (void) krb5_seteuid((uid_t)0);
- pty_logwtmp(ttyline, "", "");
- }
- if (have_creds) {
-#ifdef GSSAPI
- krb5_cc_destroy(kcontext, ccache);
-#endif
- }
- /* beware of flushing buffers after a SIGPIPE */
- _exit(status);
-}
-
-void
-myoob(sig)
- int sig;
-{
- char *cp, *cs;
-#ifndef strpbrk
- extern char *strpbrk();
-#endif
-
- /* only process if transfer occurring */
- if (!transflag)
- return;
- cp = tmpline;
- if (ftpd_getline(cp, sizeof(tmpline), stdin) == NULL) {
- reply(221, "You could at least say goodbye.");
- dologout(0);
- }
- upper(cp);
- if ((cs = strpbrk(cp, "\r\n")))
- *cs++ = '\0';
- if (strcmp(cp, "ABOR") == 0) {
- tmpline[0] = '\0';
- reply(426, "Transfer aborted. Data connection closed.");
- reply(226, "Abort successful");
- siglongjmp(urgcatch, 1);
- }
- if (strcmp(cp, "STAT") == 0) {
- if (file_size != (off_t) -1)
- reply(213, "Status: %lu of %lu bytes transferred",
- (unsigned long) byte_count,
- (unsigned long) file_size);
- else
- reply(213, "Status: %lu bytes transferred",
- (unsigned long) byte_count);
- }
-}
-
-/*
- * Note: a response of 425 is not mentioned as a possible response to
- * the PASV command in RFC959. However, it has been blessed as
- * a legitimate response by Jon Postel in a telephone conversation
- * with Rick Adams on 25 Jan 89.
- */
-void
-passive()
-{
- int len;
- register char *p, *a;
-
- pdata = socket(AF_INET, SOCK_STREAM, 0);
- if (pdata < 0) {
- perror_reply(425, "Can't open passive connection");
- return;
- }
- pasv_addr = ctrl_addr;
- pasv_addr.sin_port = 0;
- (void) krb5_seteuid((uid_t)0);
- if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
- (void) krb5_seteuid((uid_t)pw->pw_uid);
- goto pasv_error;
- }
- if (krb5_seteuid((uid_t)pw->pw_uid)) {
- fatal("seteuid user");
- }
- len = sizeof(pasv_addr);
- if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
- goto pasv_error;
- if (listen(pdata, 1) < 0)
- goto pasv_error;
- a = (char *) &pasv_addr.sin_addr;
- p = (char *) &pasv_addr.sin_port;
-
-#define UC(b) (((int) b) & 0xff)
-
- reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
- UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
- return;
-
-pasv_error:
- (void) close(pdata);
- pdata = -1;
- perror_reply(425, "Can't open passive connection");
- return;
-}
-
-/*
- * Generate unique name for file with basename "local".
- * The file named "local" is already known to exist.
- * Generates failure reply on error.
- */
-static char *
-gunique(local)
- char *local;
-{
- static char new[MAXPATHLEN];
- struct stat st;
- char *cp = strrchr(local, '/');
- int count = 0;
-
- if (cp)
- *cp = '\0';
- if (stat(cp ? local : ".", &st) < 0) {
- perror_reply(553, cp ? local : ".");
- return((char *) 0);
- }
- if (cp)
- *cp = '/';
- (void) strncpy(new, local, sizeof(new) - 1);
- new[sizeof(new) - 1] = '\0';
- cp = new + strlen(new);
- *cp++ = '.';
- for (count = 1; count < 100; count++) {
- (void) snprintf(cp, sizeof(new) - (cp - new), "%d", count);
- if (stat(new, &st) < 0)
- return(new);
- }
- reply(452, "Unique file name cannot be created.");
- return((char *) 0);
-}
-
-/*
- * Format and send reply containing system error number.
- */
-void
-perror_reply(code, string)
- int code;
- char *string;
-{
- char *err_string;
- size_t extra_len;
-
- err_string = strerror(errno);
- if (err_string == NULL)
- err_string = "(unknown error)";
- extra_len = strlen(err_string) + sizeof("(truncated): .");
-
- /*
- * XXX knows about FTP_BUFSIZ in reply()
- */
- if (strlen(string) + extra_len > FTP_BUFSIZ) {
- reply(code, "(truncated)%.*s: %s.",
- (int) (FTP_BUFSIZ - extra_len), string, err_string);
- } else {
- reply(code, "%s: %s.", string, err_string);
- }
-}
-
-void
-auth(atype)
-char *atype;
-{
- if (auth_type)
- reply(534, "Authentication type already set to %s", auth_type);
- else
-#ifdef GSSAPI
- if (strcmp(atype, "GSSAPI") == 0)
- reply(334, "Using authentication type %s; ADAT must follow",
- temp_auth_type = atype);
- else
-#endif /* GSSAPI */
- /* Other auth types go here ... */
- reply(504, "Unknown authentication type: %s", atype);
-}
-
-int
-auth_data(adata)
-char *adata;
-{
- int kerror, length;
-
- if (auth_type) {
- reply(503, "Authentication already established");
- return(0);
- }
- if (!temp_auth_type) {
- reply(503, "Must identify AUTH type before ADAT");
- return(0);
- }
-#ifdef GSSAPI
- if (strcmp(temp_auth_type, "GSSAPI") == 0) {
- int replied = 0;
- int found = 0;
- gss_cred_id_t server_creds, deleg_creds;
- gss_name_t client;
- OM_uint32 ret_flags;
- int rad_len;
- gss_buffer_desc name_buf;
- gss_name_t server_name;
- OM_uint32 acquire_maj, acquire_min, accept_maj, accept_min,
- stat_maj, stat_min;
- gss_OID mechid;
- gss_buffer_desc tok, out_tok;
- char gbuf[FTP_BUFSIZ];
- u_char gout_buf[FTP_BUFSIZ];
- char localname[MAXHOSTNAMELEN];
- char service_name[MAXHOSTNAMELEN+10];
- char **gservice;
- struct hostent *hp;
- stat_maj = 0;
- accept_maj = 0;
- acquire_maj = 0;
-
- kerror = radix_encode(adata, gout_buf, &length, 1);
- if (kerror) {
- reply(501, "Couldn't decode ADAT (%s)",
- radix_error(kerror));
- syslog(LOG_ERR, "Couldn't decode ADAT (%s)",
- radix_error(kerror));
- return(0);
- }
- tok.value = gout_buf;
- tok.length = length;
-
- if (gethostname(localname, MAXHOSTNAMELEN)) {
- reply(501, "couldn't get local hostname (%d)\n", errno);
- syslog(LOG_ERR, "Couldn't get local hostname (%d)", errno);
- return 0;
- }
- if (!(hp = gethostbyname(localname))) {
- reply(501, "couldn't canonicalize local hostname\n");
- syslog(LOG_ERR, "Couldn't canonicalize local hostname");
- return 0;
- }
- strncpy(localname, hp->h_name, sizeof(localname) - 1);
- localname[sizeof(localname) - 1] = '\0';
-
- for (gservice = gss_services; *gservice; gservice++) {
- snprintf(service_name, sizeof(service_name),
- "%s@%s", *gservice, localname);
- name_buf.value = service_name;
- name_buf.length = strlen(name_buf.value) + 1;
- if (debug)
- syslog(LOG_INFO, "importing <%s>", service_name);
- stat_maj = gss_import_name(&stat_min, &name_buf,
- gss_nt_service_name,
- &server_name);
- if (stat_maj != GSS_S_COMPLETE) {
- reply_gss_error(501, stat_maj, stat_min,
- "importing name");
- syslog(LOG_ERR, "gssapi error importing name");
- return 0;
- }
-
- acquire_maj = gss_acquire_cred(&acquire_min, server_name, 0,
- GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
- &server_creds, NULL, NULL);
- (void) gss_release_name(&stat_min, &server_name);
-
- if (acquire_maj != GSS_S_COMPLETE)
- continue;
-
- found++;
-
- gcontext = GSS_C_NO_CONTEXT;
-
- accept_maj = gss_accept_sec_context(&accept_min,
- &gcontext, /* context_handle */
- server_creds, /* verifier_cred_handle */
- &tok, /* input_token */
- GSS_C_NO_CHANNEL_BINDINGS, /* channel bindings */
- &client, /* src_name */
- &mechid, /* mech_type */
- &out_tok, /* output_token */
- &ret_flags,
- NULL, /* ignore time_rec */
- &deleg_creds /* forwarded credentials */
- );
- if (accept_maj==GSS_S_COMPLETE||accept_maj==GSS_S_CONTINUE_NEEDED)
- break;
- }
-
- if (found) {
- if (accept_maj!=GSS_S_COMPLETE && accept_maj!=GSS_S_CONTINUE_NEEDED) {
- reply_gss_error(535, accept_maj, accept_min,
- "accepting context");
- syslog(LOG_ERR, "failed accepting context");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min,
- &deleg_creds);
- return 0;
- }
- } else {
- /* Kludge to make sure the right error gets reported, so we don't *
- * get those nasty "error: no error" messages. */
- if(stat_maj != GSS_S_COMPLETE)
- reply_gss_error(501, stat_maj, stat_min,
- "acquiring credentials");
- else
- reply_gss_error(501, acquire_maj, acquire_min,
- "acquiring credentials");
- syslog(LOG_ERR, "gssapi error acquiring credentials");
- return 0;
- }
-
- if (out_tok.length) {
- if (out_tok.length >= ((FTP_BUFSIZ - sizeof("ADAT="))
- / 4 * 3)) {
- secure_error("ADAT: reply too long");
- syslog(LOG_ERR, "ADAT: reply too long");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min,
- &deleg_creds);
- return(0);
- }
-
- rad_len = out_tok.length;
- kerror = radix_encode(out_tok.value, gbuf,
- &rad_len, 0);
- out_tok.length = rad_len;
- if (kerror) {
- secure_error("Couldn't encode ADAT reply (%s)",
- radix_error(kerror));
- syslog(LOG_ERR, "couldn't encode ADAT reply");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min,
- &deleg_creds);
- return(0);
- }
- if (accept_maj == GSS_S_COMPLETE) {
- reply(235, "ADAT=%s", gbuf);
- } else {
- /* If the server accepts the security data, and
- requires additional data, it should respond
- with reply code 335. */
- reply(335, "ADAT=%s", gbuf);
- }
- replied = 1;
- (void) gss_release_buffer(&stat_min, &out_tok);
- }
- if (accept_maj == GSS_S_COMPLETE) {
- /* GSSAPI authentication succeeded */
- stat_maj = gss_display_name(&stat_min, client,
- &client_name, &mechid);
- if (stat_maj != GSS_S_COMPLETE) {
- /* "If the server rejects the security data (if
- a checksum fails, for instance), it should
- respond with reply code 535." */
- reply_gss_error(535, stat_maj, stat_min,
- "extracting GSSAPI identity name");
- log_gss_error(LOG_ERR, stat_maj, stat_min,
- "gssapi error extracting identity");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min,
- &deleg_creds);
- return 0;
- }
- auth_type = temp_auth_type;
- temp_auth_type = NULL;
-
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG) {
- if (want_creds)
- ftpd_gss_convert_creds(client_name.value,
- deleg_creds);
- (void) gss_release_cred(&stat_min, &deleg_creds);
- }
-
- /* If the server accepts the security data, but does
- not require any additional data (i.e., the security
- data exchange has completed successfully), it must
- respond with reply code 235. */
- if (!replied)
- {
- if (ret_flags & GSS_C_DELEG_FLAG && !have_creds)
- reply(235, "GSSAPI Authentication succeeded, but could not accept forwarded credentials");
- else
- reply(235, "GSSAPI Authentication succeeded");
- }
-
- return(1);
- } else if (accept_maj == GSS_S_CONTINUE_NEEDED) {
- /* If the server accepts the security data, and
- requires additional data, it should respond with
- reply code 335. */
- if (!replied)
- reply(335, "more data needed");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min, &deleg_creds);
- return(0);
- } else {
- /* "If the server rejects the security data (if
- a checksum fails, for instance), it should
- respond with reply code 535." */
- reply_gss_error(535, stat_maj, stat_min,
- "GSSAPI failed processing ADAT");
- syslog(LOG_ERR, "GSSAPI failed processing ADAT");
- (void) gss_release_cred(&stat_min, &server_creds);
- if (ret_flags & GSS_C_DELEG_FLAG)
- (void) gss_release_cred(&stat_min, &deleg_creds);
- return(0);
- }
- }
-#endif /* GSSAPI */
- /* Other auth types go here ... */
- /* Also need to check authorization, but that is done in user() */
- return(0);
-}
-
-static char *onefile[] = {
- "",
- 0
-};
-
-/* returns:
- * n>=0 on success
- * -1 on error
- * -2 on security error
- *
- * XXX callers need to limit total length of output string to
- * FTP_BUFSIZ
- */
-static int
-secure_fprintf(FILE *stream, char *fmt, ...)
-#if !defined(__cplusplus) && (__GNUC__ > 2)
- __attribute__((__format__(__printf__, 2, 3)))
-#endif
- ;
-
-static int
-secure_fprintf(FILE *stream, char *fmt, ...)
-{
- char s[FTP_BUFSIZ];
- int rval;
- va_list ap;
-
- va_start(ap, fmt);
- if (dlevel == PROT_C) rval = vfprintf(stream, fmt, ap);
- else {
- vsnprintf(s, sizeof(s), fmt, ap);
- rval = secure_write(fileno(stream), s, strlen(s));
- }
- va_end(ap);
-
- return(rval);
-}
-
-void
-send_file_list(whichfiles)
- char *whichfiles;
-{
- struct stat st;
- DIR *dirp = NULL;
- struct dirent *dir;
- FILE *volatile dout = NULL;
- register char **volatile dirlist, *dirname;
- volatile int simple = 0;
-#ifndef strpbrk
- char *strpbrk();
-#endif
- volatile int ret = 0;
-
- if (strpbrk(whichfiles, "~{[*?") != NULL) {
- extern char **ftpglob(), *globerr;
-
- globerr = NULL;
- dirlist = ftpglob(whichfiles);
- if (globerr != NULL) {
- reply(550, globerr);
- return;
- } else if (dirlist == NULL) {
- errno = ENOENT;
- perror_reply(550, whichfiles);
- return;
- }
- } else {
- onefile[0] = whichfiles;
- dirlist = onefile;
- simple = 1;
- }
-
- if (sigsetjmp(urgcatch, 1)) {
- transflag = 0;
- (void)secure_flush(fileno(dout));
- return;
- }
- while ((dirname = *dirlist++)) {
- if (stat(dirname, &st) < 0) {
- /*
- * If user typed "ls -l", etc, and the client
- * used NLST, do what the user meant.
- */
- if (dirname[0] == '-' && *dirlist == NULL &&
- transflag == 0) {
- retrieve("/bin/ls %s", dirname);
- return;
- }
- perror_reply(550, whichfiles);
- if (dout != NULL) {
- (void) fclose(dout);
- transflag = 0;
- data = -1;
- pdata = -1;
- }
- return;
- }
-
- if ((st.st_mode&S_IFMT) == S_IFREG) {
- if (dout == NULL) {
- dout = dataconn("file list", (off_t)-1, "w");
- if (dout == NULL)
- return;
- transflag++;
- }
- if ((ret = secure_fprintf(dout, "%s%s\n", dirname,
- type == TYPE_A ? "\r" : "")) < 0)
- goto data_err;
- byte_count += strlen(dirname) + 1;
- continue;
- } else if ((st.st_mode&S_IFMT) != S_IFDIR)
- continue;
-
- if ((dirp = opendir(dirname)) == NULL)
- continue;
-
- while ((dir = readdir(dirp)) != NULL) {
- char nbuf[MAXPATHLEN];
-
- if (dir->d_name[0] == '.' && dir->d_name[1] == '\0')
- continue;
- if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
- dir->d_name[2] == '\0')
- continue;
-
- if (strlen(dirname) + strlen(dir->d_name)
- + 1 /* slash */
- + 2 /* CRLF */
- + 1 > sizeof(nbuf)) {
- syslog(LOG_ERR,
- "send_file_list: pathname too long");
- ret = -2; /* XXX */
- goto data_err;
- }
- snprintf(nbuf, sizeof(nbuf), "%s/%s",
- dirname, dir->d_name);
-
- /*
- * We have to do a stat to insure it's
- * not a directory or special file.
- */
- if (simple || (stat(nbuf, &st) == 0 &&
- (st.st_mode&S_IFMT) == S_IFREG)) {
- if (dout == NULL) {
- dout = dataconn("file list", (off_t)-1,
- "w");
- if (dout == NULL)
- return;
- transflag++;
- }
- if (nbuf[0] == '.' && nbuf[1] == '/')
- {
- if ((ret = secure_fprintf(dout, "%s%s\n", &nbuf[2],
- type == TYPE_A ? "\r" : "")) < 0)
- goto data_err;
- }
- else
- if ((ret = secure_fprintf(dout, "%s%s\n", nbuf,
- type == TYPE_A ? "\r" : "")) < 0)
- goto data_err;
- byte_count += strlen(nbuf) + 1;
- }
- }
- (void) closedir(dirp);
- }
- if (dout != NULL ) {
- ret = secure_write(fileno(dout), "", 0);
- if (ret >= 0)
- ret = secure_flush(fileno(dout));
- }
-data_err:
- if (dout == NULL)
- reply(550, "No files found.");
- else if (ferror(dout) != 0 || ret == EOF)
- perror_reply(550, "Data connection");
- else if (ret != -2)
- reply(226, "Transfer complete.");
-
- transflag = 0;
- if (dout != NULL)
- (void) fclose(dout);
- data = -1;
- pdata = -1;
-}
-
-#ifdef SETPROCTITLE
-/*
- * clobber argv so ps will show what we're doing.
- * (stolen from sendmail)
- * warning, since this is usually started from inetd.conf, it
- * often doesn't have much of an environment or arglist to overwrite.
- */
-
-setproctitle(buf)
-char *buf;
-{
- register char *p, *bp, ch;
- register int i;
-
- /* make ps print our process name */
- p = Argv[0];
- *p++ = '-';
-
- i = strlen(buf);
- if (i > LastArgv - p - 2) {
- i = LastArgv - p - 2;
- buf[i] = '\0';
- }
- bp = buf;
- while (ch = *bp++)
- if (ch != '\n' && ch != '\r')
- *p++ = ch;
- while (p < LastArgv)
- *p++ = ' ';
-}
-#endif /* SETPROCTITLE */
-
-#ifdef GSSAPI
-/* A more general callback would probably use a void*, but currently I
- only need an int in both cases. */
-static void with_gss_error_text(void (*cb)(const char *, int, int),
- OM_uint32 maj_stat, OM_uint32 min_stat,
- int misc);
-
-static void
-log_gss_error_1(const char *msg, int severity, int is_major)
-{
- syslog(severity, "... GSSAPI error %s: %s",
- is_major ? "major" : "minor", msg);
-}
-
-static void
-log_gss_error(int severity, OM_uint32 maj_stat, OM_uint32 min_stat,
- const char *s)
-{
- syslog(severity, s);
- with_gss_error_text(log_gss_error_1, maj_stat, min_stat, severity);
-}
-
-static void
-reply_gss_error_1(const char *msg, int code, int is_major)
-{
- lreply(code, "GSSAPI error %s: %s",
- is_major ? "major" : "minor", msg);
-}
-
-void
-reply_gss_error(int code, OM_uint32 maj_stat, OM_uint32 min_stat, char *s)
-{
- with_gss_error_text(reply_gss_error_1, maj_stat, min_stat, code);
- reply(code, "GSSAPI error: %s", s);
-}
-
-static void with_gss_error_text(void (*cb)(const char *, int, int),
- OM_uint32 maj_stat, OM_uint32 min_stat,
- int misc)
-{
- /* a lot of work just to report the error */
- OM_uint32 gmaj_stat, gmin_stat;
- gss_buffer_desc msg;
- OM_uint32 msg_ctx;
- msg_ctx = 0;
- while (!msg_ctx) {
- gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
- GSS_C_GSS_CODE,
- GSS_C_NULL_OID,
- &msg_ctx, &msg);
- if ((gmaj_stat == GSS_S_COMPLETE)||
- (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
- (*cb)((char*)msg.value, misc, 1);
- (void) gss_release_buffer(&gmin_stat, &msg);
- }
- if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
- break;
- }
- msg_ctx = 0;
- while (!msg_ctx) {
- gmaj_stat = gss_display_status(&gmin_stat, min_stat,
- GSS_C_MECH_CODE,
- GSS_C_NULL_OID,
- &msg_ctx, &msg);
- if ((gmaj_stat == GSS_S_COMPLETE)||
- (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
- (*cb)((char*)msg.value, misc, 0);
- (void) gss_release_buffer(&gmin_stat, &msg);
- }
- if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
- break;
- }
-}
-
-void
-secure_gss_error(maj_stat, min_stat, s)
-OM_uint32 maj_stat, min_stat;
-char *s;
-{
- reply_gss_error(535, maj_stat, min_stat, s);
- return;
-}
-
-
-/* ftpd_gss_userok -- hide details of getting the name and verifying it */
-/* returns 0 for OK */
-static int
-ftpd_gss_userok(gclient_name, name)
- gss_buffer_t gclient_name;
- char *name;
-{
- int retval = -1;
- krb5_principal p;
-
- if (krb5_parse_name(kcontext, gclient_name->value, &p) != 0)
- return -1;
- if (krb5_kuserok(kcontext, p, name))
- retval = 0;
- else
- retval = 1;
- krb5_free_principal(kcontext, p);
- return retval;
-}
-
-/* ftpd_gss_convert_creds -- write out forwarded creds */
-/* (code lifted from login.krb5) */
-static void
-ftpd_gss_convert_creds(name, creds)
- char *name;
- gss_cred_id_t creds;
-{
- OM_uint32 major_status, minor_status;
- krb5_principal me;
- char ccname[MAXPATHLEN];
-
- /* Set up ccache */
- if (krb5_parse_name(kcontext, name, &me))
- return;
-
- snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_ftpd%ld",
- (long) getpid());
- if (krb5_cc_resolve(kcontext, ccname, &ccache))
- return;
- if (krb5_cc_initialize(kcontext, ccache, me))
- return;
-
- /* Copy GSS creds into ccache */
- major_status = gss_krb5_copy_ccache(&minor_status, creds, ccache);
- if (major_status != GSS_S_COMPLETE)
- goto cleanup;
-
- have_creds = 1;
- return;
-
-cleanup:
- krb5_cc_destroy(kcontext, ccache);
-}
-
-
-#endif /* GSSAPI */