Remove all traces of setjmp/longjmp.

Handle those command that is needed in oobhandler,
those are ABOR, STAT, ENC, CONF, MIC.
add options to turn off insecure OOB handling and document the option

Changes inspired by openbsd and netbsd changes but quite diffrent is
most places since the code no longer look and is structured the same
way.


git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@14136 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Love Hörnquist Åstrand
2004-08-20 13:31:20 +00:00
parent 8763264587
commit bf0ab85d2e
5 changed files with 238 additions and 208 deletions

View File

@@ -48,7 +48,6 @@
#include <stdio.h>
#include <stdarg.h>
#include <setjmp.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
@@ -129,10 +128,8 @@ extern struct passwd *pw;
extern int guest;
extern int logging;
extern int type;
extern int oobflag;
extern off_t file_size;
extern off_t byte_count;
extern jmp_buf urgcatch;
extern int form;
extern int debug;
@@ -142,7 +139,6 @@ extern int pdata;
extern char hostname[], remotehost[];
extern char proctitle[];
extern int usedefault;
extern int transflag;
extern char tmpline[];
#endif /* _EXTERN_H_ */

View File

@@ -47,6 +47,9 @@ RCSID("$Id$");
off_t restart_point;
static int hasyyerrored;
static int cmd_type;
static int cmd_form;
static int cmd_bytesz;
@@ -303,15 +306,6 @@ cmd
}
| sTAT CRLF
{
if(oobflag){
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);
}else
statcmd();
}
| DELE SP pathname CRLF check_login_no_guest
@@ -337,13 +331,7 @@ cmd
}
| ABOR CRLF
{
if(oobflag){
reply(426, "Transfer aborted. Data connection closed.");
reply(226, "Abort successful");
oobflag = 0;
longjmp(urgcatch, 1);
}else
reply(225, "ABOR command successful.");
reply(225, "ABOR command successful.");
}
| CWD CRLF check_login
{
@@ -914,8 +902,6 @@ check_secure : /* empty */
%%
extern jmp_buf errcatch;
#define CMD 0 /* beginning of command */
#define ARGS 1 /* expect miscellaneous arguments */
#define STR1 2 /* expect SP followed by STRING */
@@ -1034,15 +1020,13 @@ ftpd_getline(char *s, int n)
char *cs;
cs = s;
/* tmpline may contain saved command from urgent mode interruption */
/* might still be data within the security MIC/CONF/ENC */
if(ftp_command){
strlcpy(s, ftp_command, n);
if (debug)
syslog(LOG_DEBUG, "command: %s", s);
#ifdef XXX
fprintf(stderr, "%s\n", s);
#endif
return s;
strlcpy(s, ftp_command, n);
if (debug)
syslog(LOG_DEBUG, "command: %s", s);
return s;
}
while ((c = getc(stdin)) != EOF) {
c &= 0377;
@@ -1127,6 +1111,8 @@ yylex(void)
switch (state) {
case CMD:
hasyyerrored = 0;
signal(SIGALRM, toolong);
alarm((unsigned) ftpd_timeout);
if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
@@ -1154,8 +1140,8 @@ yylex(void)
if (p != 0) {
if (p->implemented == 0) {
nack(p->name);
longjmp(errcatch,0);
/* NOTREACHED */
hasyyerrored = 1;
break;
}
state = p->state;
yylval.s = p->name;
@@ -1180,8 +1166,8 @@ yylex(void)
if (p->implemented == 0) {
state = CMD;
nack(p->name);
longjmp(errcatch,0);
/* NOTREACHED */
hasyyerrored = 1;
break;
}
state = p->state;
yylval.s = p->name;
@@ -1329,12 +1315,27 @@ yylex(void)
default:
fatal("Unknown state in scanner.");
}
yyerror((char *) 0);
yyerror(NULL);
state = CMD;
longjmp(errcatch,0);
return (0);
}
}
/* ARGSUSED */
void
yyerror(char *s)
{
char *cp;
if (hasyyerrored)
return;
if ((cp = strchr(cbuf,'\n')))
*cp = '\0';
reply(500, "'%s': command not understood.", cbuf);
hasyyerrored = 1;
}
static char *
copy(char *s)
{

View File

@@ -48,6 +48,7 @@
.Op Fl T Ar maxtimeout
.Op Fl t Ar timeout
.Op Fl -gss-bindings
.Op Fl I | Fl -no-insecure-oob
.Op Fl u Ar default umask
.Op Fl B | Fl -builtin-ls
.Op Fl -good-chars= Ns Ar string
@@ -150,6 +151,13 @@ use built-in ls to list files
.Fl -good-chars= Ns Ar string
.Xc
allowed anonymous upload filename chars
.It Xo
.Fl I
.Fl -no-insecure-oob
.Xc
don't allow insecure out of band.
Heimdal ftp client before 0.7 doesn't support secure oob, so turning
on this options makes them no longer work.
.El
.Pp
The file

View File

@@ -61,8 +61,6 @@ struct sockaddr_storage pasv_addr_ss;
struct sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss;
int data;
jmp_buf errcatch, urgcatch;
int oobflag;
int logged_in;
struct passwd *pw;
int debug = 0;
@@ -78,7 +76,9 @@ int stru; /* avoid C keyword */
int mode;
int usedefault = 1; /* for data transfers */
int pdata = -1; /* for passive mode */
int transflag;
int allow_insecure_oob = 1;
static int transflag;
static int urgflag;
off_t file_size;
off_t byte_count;
#if !defined(CMASK) || CMASK == 0
@@ -134,6 +134,7 @@ char proctitle[BUFSIZ]; /* initial part of title */
static void ack (char *);
static void myoob (int);
static int handleoobcmd(void);
static int checkuser (char *, char *);
static int checkaccess (char *);
static FILE *dataconn (const char *, off_t, const char *);
@@ -223,6 +224,7 @@ struct getargs args[] = {
{ NULL, 'v', arg_flag, &debug, "enable debugging" },
{ "builtin-ls", 'B', arg_flag, &use_builtin_ls, "use built-in ls to list files" },
{ "good-chars", 0, arg_string, &good_chars, "allowed anonymous upload filename chars" },
{ "insecure-oob", 'I', arg_negative_flag, &allow_insecure_oob, "don't allow insecure OOB ABOR/STAT" },
#ifdef KRB5
{ "gss-bindings", 0, arg_flag, &ftp_do_gss_bindings, "Require GSS-API bindings", NULL},
#endif
@@ -429,7 +431,6 @@ main(int argc, char **argv)
#endif
);
setjmp(errcatch);
for (;;)
yyparse();
/* NOTREACHED */
@@ -1364,15 +1365,13 @@ send_data(FILE *instr, FILE *outstr)
static char *buf;
static size_t bufsize;
transflag++;
if (setjmp(urgcatch)) {
transflag = 0;
return;
}
transflag = 1;
switch (type) {
case TYPE_A:
while ((c = getc(instr)) != EOF) {
if (urgflag && handleoobcmd())
return;
byte_count++;
if(c == '\n')
sec_putc('\r', outstr);
@@ -1380,6 +1379,7 @@ send_data(FILE *instr, FILE *outstr)
}
sec_fflush(outstr);
transflag = 0;
urgflag = 0;
if (ferror(instr))
goto file_err;
if (ferror(outstr))
@@ -1389,6 +1389,7 @@ send_data(FILE *instr, FILE *outstr)
case TYPE_I:
case TYPE_L:
#if 0 /* XXX handle urg flag */
#if defined(HAVE_MMAP) && !defined(NO_MMAP)
#ifndef MAP_FAILED
#define MAP_FAILED (-1)
@@ -1412,9 +1413,11 @@ send_data(FILE *instr, FILE *outstr)
sec_fflush(outstr);
byte_count = cnt;
transflag = 0;
urgflag = 0;
}
}
}
#endif
#endif
if(transflag) {
struct stat st;
@@ -1425,14 +1428,19 @@ send_data(FILE *instr, FILE *outstr)
fstat(filefd, &st) >= 0 ? &st : NULL);
if (buf == NULL) {
transflag = 0;
urgflag = 0;
perror_reply(451, "Local resource failure: malloc");
return;
}
while ((cnt = read(filefd, buf, bufsize)) > 0 &&
sec_write(netfd, buf, cnt) == cnt)
sec_write(netfd, buf, cnt) == cnt) {
byte_count += cnt;
if (urgflag && handleoobcmd())
return;
}
sec_fflush(outstr); /* to end an encrypted stream */
transflag = 0;
urgflag = 0;
if (cnt != 0) {
if (cnt < 0)
goto file_err;
@@ -1443,17 +1451,20 @@ send_data(FILE *instr, FILE *outstr)
return;
default:
transflag = 0;
urgflag = 0;
reply(550, "Unimplemented TYPE %d in send_data", type);
return;
}
data_err:
transflag = 0;
urgflag = 0;
perror_reply(426, "Data connection");
return;
file_err:
transflag = 0;
urgflag = 0;
perror_reply(551, "Error on input file");
}
@@ -1471,16 +1482,13 @@ receive_data(FILE *instr, FILE *outstr)
static size_t bufsize;
struct stat st;
transflag++;
if (setjmp(urgcatch)) {
transflag = 0;
return (-1);
}
transflag = 1;
buf = alloc_buffer (buf, &bufsize,
fstat(fileno(outstr), &st) >= 0 ? &st : NULL);
if (buf == NULL) {
transflag = 0;
urgflag = 0;
perror_reply(451, "Local resource failure: malloc");
return -1;
}
@@ -1493,15 +1501,19 @@ receive_data(FILE *instr, FILE *outstr)
if (write(fileno(outstr), buf, cnt) != cnt)
goto file_err;
byte_count += cnt;
if (urgflag && handleoobcmd())
return (-1);
}
if (cnt < 0)
goto data_err;
transflag = 0;
urgflag = 0;
return (0);
case TYPE_E:
reply(553, "TYPE E not implemented.");
transflag = 0;
urgflag = 0;
return (-1);
case TYPE_A:
@@ -1511,6 +1523,8 @@ receive_data(FILE *instr, FILE *outstr)
while ((cnt = sec_read(fileno(instr),
buf + cr_flag,
bufsize - cr_flag)) > 0){
if (urgflag && handleoobcmd())
return (-1);
byte_count += cnt;
cnt += cr_flag;
cr_flag = 0;
@@ -1542,6 +1556,7 @@ receive_data(FILE *instr, FILE *outstr)
if (ferror(outstr))
goto file_err;
transflag = 0;
urgflag = 0;
if (bare_lfs) {
lreply(226, "WARNING! %d bare linefeeds received in ASCII mode\r\n"
" File may not have transferred correctly.\r\n",
@@ -1552,16 +1567,19 @@ receive_data(FILE *instr, FILE *outstr)
default:
reply(550, "Unimplemented TYPE %d in receive_data", type);
transflag = 0;
urgflag = 0;
return (-1);
}
data_err:
transflag = 0;
urgflag = 0;
perror_reply(426, "Data Connection");
return (-1);
file_err:
transflag = 0;
urgflag = 0;
perror_reply(452, "Error writing file");
return (-1);
}
@@ -1731,17 +1749,6 @@ nack(char *s)
reply(502, "%s command not implemented.", s);
}
/* ARGSUSED */
void
yyerror(char *s)
{
char *cp;
if ((cp = strchr(cbuf,'\n')))
*cp = '\0';
reply(500, "'%s': command not understood.", cbuf);
}
void
do_delete(char *name)
{
@@ -1880,6 +1887,7 @@ void
dologout(int status)
{
transflag = 0;
urgflag = 0;
if (logged_in) {
seteuid((uid_t)0);
ftpd_logwtmp(ttyline, "", "");
@@ -1897,51 +1905,72 @@ dologout(int status)
void abor(void)
{
if (!transflag)
return;
reply(426, "Transfer aborted. Data connection closed.");
reply(226, "Abort successful");
transflag = 0;
}
static void
myoob(int signo)
{
#if 0
urgflag = 1;
}
static char *
mec_space(char *p)
{
while(isspace(*(unsigned char *)p))
p++;
return p;
}
static int
handleoobcmd(void)
{
char *cp;
#endif
/* only process if transfer occurring */
if (!transflag)
return;
return 0;
/* This is all XXX */
oobflag = 1;
/* if the command resulted in a new command,
parse that as well */
do{
yyparse();
} while(ftp_command);
oobflag = 0;
urgflag = 0;
#if 0
cp = tmpline;
if (ftpd_getline(cp, 7) == NULL) {
if (ftpd_getline(cp, sizeof(tmpline)) == NULL) {
reply(221, "You could at least say goodbye.");
dologout(0);
}
upper(cp);
if (strcmp(cp, "ABOR\r\n") == 0) {
tmpline[0] = '\0';
reply(426, "Transfer aborted. Data connection closed.");
reply(226, "Abort successful");
longjmp(urgcatch, 1);
if (strncasecmp("MIC", cp, 3) == 0) {
mec(mec_space(cp + 3), prot_safe);
} else if (strncasecmp("CONF", cp, 4) == 0) {
mec(mec_space(cp + 4), prot_confidential);
} else if (strncasecmp("ENC", cp, 3) == 0) {
mec(mec_space(cp + 3), prot_private);
} else if (!allow_insecure_oob) {
reply(533, "Command protection level denied "
"for paranoid reasons.");
goto out;
}
if (strcmp(cp, "STAT\r\n") == 0) {
if (secure_command())
cp = ftp_command;
if (strcasecmp(cp, "ABOR\r\n") == 0) {
abor();
} else if (strcasecmp(cp, "STAT\r\n") == 0) {
if (file_size != (off_t) -1)
reply(213, "Status: %ld of %ld bytes transferred",
(long)byte_count,
(long)file_size);
else
reply(213, "Status: %ld bytes transferred"
reply(213, "Status: %ld bytes transferred",
(long)byte_count);
}
#endif
out:
return (transflag == 0);
}
/*
@@ -2184,139 +2213,136 @@ list_file(char *file)
void
send_file_list(char *whichf)
{
struct stat st;
DIR *dirp = NULL;
struct dirent *dir;
FILE *dout = NULL;
char **dirlist, *dirname;
int simple = 0;
int freeglob = 0;
glob_t gl;
char buf[MaxPathLen];
struct stat st;
DIR *dirp = NULL;
struct dirent *dir;
FILE *dout = NULL;
char **dirlist, *dirname;
int simple = 0;
int freeglob = 0;
glob_t gl;
char buf[MaxPathLen];
if (strpbrk(whichf, "~{[*?") != NULL) {
int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|
if (strpbrk(whichf, "~{[*?") != NULL) {
int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|
#ifdef GLOB_MAXPATH
GLOB_MAXPATH
GLOB_MAXPATH
#else
GLOB_LIMIT
GLOB_LIMIT
#endif
;
;
memset(&gl, 0, sizeof(gl));
freeglob = 1;
if (glob(whichf, flags, 0, &gl)) {
reply(550, "not found");
goto out;
} else if (gl.gl_pathc == 0) {
errno = ENOENT;
perror_reply(550, whichf);
goto out;
}
dirlist = gl.gl_pathv;
} else {
onefile[0] = whichf;
dirlist = onefile;
simple = 1;
}
if (setjmp(urgcatch)) {
transflag = 0;
goto out;
}
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) {
list_file(dirname);
goto out;
}
perror_reply(550, whichf);
if (dout != NULL) {
fclose(dout);
transflag = 0;
data = -1;
pdata = -1;
}
goto out;
}
if (S_ISREG(st.st_mode)) {
if (dout == NULL) {
dout = dataconn("file list", (off_t)-1, "w");
if (dout == NULL)
goto out;
transflag++;
}
snprintf(buf, sizeof(buf), "%s%s\n", dirname,
type == TYPE_A ? "\r" : "");
sec_write(fileno(dout), buf, strlen(buf));
byte_count += strlen(dirname) + 1;
continue;
} else if (!S_ISDIR(st.st_mode))
continue;
if ((dirp = opendir(dirname)) == NULL)
continue;
while ((dir = readdir(dirp)) != NULL) {
char nbuf[MaxPathLen];
if (!strcmp(dir->d_name, "."))
continue;
if (!strcmp(dir->d_name, ".."))
continue;
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 &&
S_ISREG(st.st_mode))) {
if (dout == NULL) {
dout = dataconn("file list", (off_t)-1, "w");
if (dout == NULL)
memset(&gl, 0, sizeof(gl));
freeglob = 1;
if (glob(whichf, flags, 0, &gl)) {
reply(550, "not found");
goto out;
} else if (gl.gl_pathc == 0) {
errno = ENOENT;
perror_reply(550, whichf);
goto out;
transflag++;
}
if(strncmp(nbuf, "./", 2) == 0)
snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2,
type == TYPE_A ? "\r" : "");
else
snprintf(buf, sizeof(buf), "%s%s\n", nbuf,
type == TYPE_A ? "\r" : "");
sec_write(fileno(dout), buf, strlen(buf));
byte_count += strlen(nbuf) + 1;
}
dirlist = gl.gl_pathv;
} else {
onefile[0] = whichf;
dirlist = onefile;
simple = 1;
}
closedir(dirp);
}
if (dout == NULL)
reply(550, "No files found.");
else if (ferror(dout) != 0)
perror_reply(550, "Data connection");
else
reply(226, "Transfer complete.");
transflag = 0;
if (dout != NULL){
sec_write(fileno(dout), buf, 0); /* XXX flush */
fclose(dout);
}
data = -1;
pdata = -1;
while ((dirname = *dirlist++)) {
if (urgflag && handleoobcmd())
goto out;
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) {
list_file(dirname);
goto out;
}
perror_reply(550, whichf);
goto out;
}
if (S_ISREG(st.st_mode)) {
if (dout == NULL) {
dout = dataconn("file list", (off_t)-1, "w");
if (dout == NULL)
goto out;
transflag = 1;
}
snprintf(buf, sizeof(buf), "%s%s\n", dirname,
type == TYPE_A ? "\r" : "");
sec_write(fileno(dout), buf, strlen(buf));
byte_count += strlen(dirname) + 1;
continue;
} else if (!S_ISDIR(st.st_mode))
continue;
if ((dirp = opendir(dirname)) == NULL)
continue;
while ((dir = readdir(dirp)) != NULL) {
char nbuf[MaxPathLen];
if (urgflag && handleoobcmd())
goto out;
if (!strcmp(dir->d_name, "."))
continue;
if (!strcmp(dir->d_name, ".."))
continue;
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 &&
S_ISREG(st.st_mode))) {
if (dout == NULL) {
dout = dataconn("file list", (off_t)-1, "w");
if (dout == NULL)
goto out;
transflag = 1;
}
if(strncmp(nbuf, "./", 2) == 0)
snprintf(buf, sizeof(buf), "%s%s\n", nbuf +2,
type == TYPE_A ? "\r" : "");
else
snprintf(buf, sizeof(buf), "%s%s\n", nbuf,
type == TYPE_A ? "\r" : "");
sec_write(fileno(dout), buf, strlen(buf));
byte_count += strlen(nbuf) + 1;
}
}
closedir(dirp);
}
if (dout == NULL)
reply(550, "No files found.");
else if (ferror(dout) != 0)
perror_reply(550, "Data connection");
else
reply(226, "Transfer complete.");
out:
if (freeglob) {
freeglob = 0;
globfree(&gl);
}
transflag = 0;
if (dout != NULL){
sec_write(fileno(dout), buf, 0); /* XXX flush */
fclose(dout);
}
data = -1;
pdata = -1;
if (freeglob) {
freeglob = 0;
globfree(&gl);
}
}

View File

@@ -111,7 +111,6 @@
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>