COMMAND
catdoc
SYSTEMS AFFECTED
Systems with catdoc-0.90
PROBLEM
Duncan Simpson found following. catdoc-0.90 is full of buffer
overruns. This could be a security problem if catdoc is used with
privilege users do not have for automated indexing purposes or
otherwise used with raised privilege. There are lots of overruns
for bad guys to exploit.
Kragen added this is not just a security problem if catdoc is run
with "privileges users don't have" --- it's a security problem if
you accept any documents from the outside world and then try to
read them with catdoc, without first checking them to see if they
have buffer-overflow attempts in them. Since, presumably, the
usual reason one runs catdoc is that one person created a document
with Microsoft products and another person, without Microsoft
products available, tries to read that document, this is
essentially a constant security hole.
SOLUTION
The patch below fixes all of the bugs:
--- src/catdoc.h~ Fri Sep 11 12:32:23 1998
+++ src/catdoc.h Thu Nov 5 03:28:36 1998
@@ -113,7 +113,7 @@
extern char* map_path, *charset_path;
extern int signature_check;
extern int unknown_as_hex;
-void find_file(char *full_path,const char *name, const char *path);
+void find_file(char *full_path,const char *name, const char *path, int size);
void read_config_file(const char *filename);
SUBSTMAP read_substmap(const char* filename);
--- src/catdoc.c.dist Thu Nov 5 02:46:11 1998
+++ src/catdoc.c Thu Nov 5 04:13:43 1998
@@ -25,6 +25,20 @@
char **_argv;
int _argc;
#endif
+
+
+/* Safe version of strcat(strcpy(<fixed size buf>, <...>), <...>) */
+static const char *safe_cpy_cat(char *buf, int bufsiz, const char *cp, const char *ad)
+{
+ int l;
+
+ l=strlen(ad);
+ strncpy(buf, cp, bufsiz-l-1);
+ buf[bufsiz-l]='\0'; /* Ensure terminated */
+ strcat(buf, ad); /* Safe because bufsiz-l is maximum start */
+ return buf;
+}
+
/**************************************************************/
/* Main program */
/* Processes options, reads charsets files and substitution */
@@ -42,7 +56,7 @@
#endif
read_config_file(SYSTEMRC);
#ifdef USERRC
-find_file(tempname,USERRC,getenv("HOME"));
+find_file(tempname,USERRC,getenv("HOME"), sizeof(tempname));
if (*tempname) {
read_config_file(tempname);
}
@@ -97,9 +111,9 @@
target_charset= make_reverse_map(tmp_charset);
free(tmp_charset);
-spec_chars=read_substmap(strcat(strcpy(tempname,format_name),SPEC_EXT));
+spec_chars=read_substmap(safe_cpy_cat(tempname, sizeof(tempname), format_name, SPEC_EXT));
if (!spec_chars) exit(1);
-replacements=read_substmap(strcat(strcpy(tempname,format_name),REPL_EXT));
+replacements=read_substmap(safe_cpy_cat(tempname, sizeof(tempname), format_name, REPL_EXT));
if (!replacements) exit(1);
if (!isatty(fileno(stdout))) {
--- src/charsets.c.dist Thu Nov 5 02:46:11 1998
+++ src/charsets.c Thu Nov 5 04:14:04 1998
@@ -60,11 +60,15 @@
short int *new=calloc(sizeof(short int),256);
int c;
long int uc;
- strcpy (namebuf,filename);
- strcat(namebuf,CHARSET_EXT);
- find_file(filebuf,namebuf,charset_path);
- if (!namebuf[0]) {
+ /* strcpy (namebuf,filename);
+ strcat(namebuf,CHARSET_EXT); */
+ strncpy(namebuf, filename, sizeof(namebuf)-4-1); /* length of CHARSET_EXT is 4 */
+ namebuf[sizeof(namebuf)-4]='\0'; /* Ensure null terminated */
+ strcat(namebuf, CHARSET_EXT); /* Safe because enough space left by strncpy */
+
+ find_file(filebuf,namebuf,charset_path, sizeof(filebuf));
+ if (!namebuf[0]) {
fprintf(stderr,"Cannot load charset %s - file not found\n",namebuf);
return NULL;
}
--- src/fileutil.c.dist Thu Nov 5 02:46:11 1998
+++ src/fileutil.c Thu Nov 5 03:30:08 1998
@@ -17,31 +17,56 @@
/* Searches for file name in specified list of directories. Sets */
/* full_path to empty string if nothing appropriate */
/************************************************************************/
-void find_file(char *full_path,const char *name, const char *path)
+void find_file(char *full_path,const char *name, const char *path, int limit)
{ const char *p;
char *q;
- char dir_sep[2]={DIR_SEP,0};
+ int l;
+
+ limit--; /* Need one byte for \0 */
for (p=path;p;p=q+1) {
q=strchr(p,LIST_SEP);
if (q) {
- strncpy(full_path,p,q-p);
+ if (q-p>limit) {
+ fprintf(stderr, "Avoid buffer overrun path component\n");
+ continue;
+ }
+ strncpy(full_path,p,q-p); /* Safe because q-p<limit */
full_path[q-p]=0;
+ l=q-p; /* Length so far is q-p */
} else {
q--;
- strcpy(full_path,p);
+ strncpy(full_path,p, limit);
+ full_path[limit-1]='\0'; /* Ensure null terminated */
+ l=strlen(full_path); /* Length so far is length of full_path */
}
/* Empty list element means current directory */
if (!*full_path) {
full_path[0]='.';
full_path[1]=0;
+ l=1; /* One character used */
#ifdef __MSDOS__
} else {
- strcpy(full_path,add_exe_path(full_path));
+ strncpy(full_path,add_exe_path(full_path), limit);
+ full_path[limit]='\0'; /* Ensure null terminated */
+ l=strlen(full_path); /* Length so far is length of full_path */
#endif
}
- strcat(full_path,dir_sep);
- strcat(full_path,name);
+ if (full_path[l-1]!=DIR_SEP)
+ {
+ if (l>limit)
+ {
+ fprintf(stderr, "Avoiding buffer overrun path component\n");
+ continue;
+ }
+ full_path[l]=DIR_SEP;
+ l++;
+ }
+ if (l>limit) {
+ fprintf(stderr, "Avoiding buffer overrun path component\n");
+ continue;
+ }
+ strncpy(full_path+l, name, limit-l);
if (access(full_path,0)==0) {
return;
}
@@ -58,9 +83,10 @@
void check_charset(char **filename,const char *charset) {
char namebuf[128];
char pathbuf[512];
- strcpy(namebuf,charset);
- strcat(namebuf,CHARSET_EXT);
- find_file(pathbuf,namebuf,charset_path);
+ strncpy(namebuf,charset, sizeof(namebuf)-4-1); /* Length of CHARSET_EXT is 4 */
+ namebuf[sizeof(namebuf)-4]='\0'; /* Ensure null terminated */
+ strcat(namebuf,CHARSET_EXT); /* Safe because strncpy leaves enough space */
+ find_file(pathbuf,namebuf,charset_path, sizeof(pathbuf));
if (*pathbuf) {
*filename=strdup(charset);
}
@@ -73,7 +99,8 @@
char *exe_dir(void) {
static char pathbuf[127];
char *q;
- strcpy(pathbuf,_argv[0]);
+ strncpy(pathbuf,_argv[0], sizeof(pathbuf)-1);
+ pathbuf[(sizeof(pathbuf)-1]='\0'; /* Ensure null terminated */
q=strrchr(pathbuf,DIR_SEP);
if (q) {
*q=0;
@@ -84,7 +111,7 @@
}
char *add_exe_path(const char *name) {
static char path[128];
- sprintf(path,name,exe_dir());
+ snprintf(path, sizeof(path), name,exe_dir());
return path;
}
#endif
--- src/substmap.c.dist Thu Nov 5 02:46:11 1998
+++ src/substmap.c Thu Nov 5 04:14:36 1998
@@ -23,7 +23,7 @@
char stopchar;
int escaped, lineno=0;
unsigned int uc;
- find_file(fullpath,filename,add_exe_path(map_path));
+ find_file(fullpath,filename,add_exe_path(map_path), sizeof(fullpath));
if (!*fullpath) {
fprintf(stderr,"Cannot find file %s\n",filename);
exit(1);
--- src/writer.c.dist Thu Nov 5 02:46:11 1998
+++ src/writer.c Thu Nov 5 04:10:17 1998
@@ -10,33 +10,46 @@
/************************************************************************/
static char buffer[512]="";
void out_char(const char *chunk) {
+ const char *p;
+ int l;
+
if (!wrap_margin) {
fputs(chunk,stdout);
return;
}
- strcat(buffer,chunk);
- if (strchr(chunk,'\n')) {
- /* End of paragraph */
- char *q = map_subst(spec_chars,'\n');
- fputs(buffer,stdout);
- *buffer=0;
- if (q) fputs(q,stdout);
- } else if (strlen(buffer)>wrap_margin) {
- char *q=buffer,*p=buffer+wrap_margin;
- while (p>buffer&&!isspace(*p)) p--;
- if (p==buffer) {
- /*worst case - nowhere to wrap. Will use brute force */
- fwrite(buffer,wrap_margin,1,stdout);
- fputc('\n',stdout);
- p=buffer+wrap_margin;
- } else {
- *p=0;p++;
- fputs(buffer,stdout);
- fputc('\n',stdout);
- }
- for(q=buffer;*p;p++,q++) *q=*p;
- *q=0;
- }
+
+ p=chunk;
+ while(*p!='\0') {
+ l=sizeof(buffer)-strlen(buffer)-1; /* Figure out remaining space */
+ if (l>strlen(p))
+ l=strlen(p); /* Limit to length of p */
+ strncat(buffer, p, l); /* Addend. Max end is sizeof(buffer)-1 */
+ buffer[sizeof(buffer)-1]='\0'; /* Ensure terminated */
+ p+=l; /* Make p point to remainder of chunk */
+
+ if (strchr(buffer,'\n')) {
+ /* End of paragraph */
+ char *q = map_subst(spec_chars,'\n');
+ fputs(buffer,stdout);
+ *buffer=0;
+ if (q) fputs(q,stdout);
+ } else if (strlen(buffer)>wrap_margin) {
+ char *q=buffer,*p=buffer+wrap_margin;
+ while (p>buffer&&!isspace(*p)) p--;
+ if (p==buffer) {
+ /*worst case - nowhere to wrap. Will use brute force */
+ fwrite(buffer,wrap_margin,1,stdout);
+ fputc('\n',stdout);
+ p=buffer+wrap_margin;
+ } else {
+ *p=0;p++;
+ fputs(buffer,stdout);
+ fputc('\n',stdout);
+ }
+ for(q=buffer;*p;p++,q++) *q=*p;
+ *q=0;
+ }
+ }
}
/**************************************************************************/
/* Converts unicode char to output charset sequence. Coversion have */
@@ -59,7 +72,7 @@
}
if ((mapped = map_subst(replacements,uc))) return mapped;
if (unknown_as_hex) {
- sprintf(hexbuf,"\\x%04X",(unsigned)uc);
+ snprintf(hexbuf, sizeof(hexbuf), "\\x%04X",(unsigned)uc); /* Be paranoid */
return hexbuf;
}
return bad_char;