ztempfile fixes (bug #686922)
Raph Levien <[email protected]>
| Newsgroups | gmane.comp.printing.ghostscript.patches |
|---|---|
| Message-ID | <[email protected]> |
This patch cleans up the behavior of the .tempfile operator, primarily
making it consistent across all the platforms, ensuring that the
TMPDIR and TEMP environment variables are effective, the system temp
directory is used when these are not specified, and the permissions
for tempfile creation work as expected (allowed in the simple case,
but closing off potential vulnerabilities in other cases. Intended as
a final fix for bug #686922.
Here are the changes in more detail:
1. The permissions behavior of .tempfile is changed so that simple
prefixes are always allowed, absolute pathname prefixes are checked
using the PermitFileWriting logic, and other cases are disallowed. In
particular, it is no longer possible to specify temp prefixes relative
to the current directory, or as subdirectories of the standard temp
directory. Both such cases are potential security holes, and are
probably not useful in any real application.
2. The meaning of gp_open_scratch_file() is made more consistent
across the platforms. In particular, it is now the clear
responsibility of this function to add the appropriate temp
directory. As such, gp_gettmpdir() is no longer exported in the public
gp.h interface (gpmisc.h is intended only for the convenience of gp_
implementations).
3. On VMS, gp_open_scratch_file() didn't previously add the
temporary directory pathname. Now it does.
4. On Windows, gp_open_scratch_file()'s behavior was somewhat
inconsistent, calling GetTempPath() only when the length of the TMPDIR
or TEMP environment variables overflowed the buffer provided. It
now additionally uses GetTempPath() in the cases where these
environment variables are not set.
5. Since gp_open_scratch_file() now takes responsibility for
determining the temporary directory, ztempfile() no longer attempts
to do this.
Testing would be appreciated on other platforms to make sure the
behavior is still as expected.
Index: doc/Language.htm
===================================================================
RCS file: /cvs/ghostscript/gs/doc/Language.htm,v
retrieving revision 1.73
diff -C2 -r1.73 Language.htm
*** doc/Language.htm 30 Jul 2003 20:24:15 -0000 1.73
--- doc/Language.htm 14 Aug 2003 23:31:03 -0000
***************
*** 1044,1050 ****
<b><tt>/tmp/gs_a1234</tt></b>.
<p>
! <li>A string that is not the beginning of an absolute file name (e.g., does
! not begin with <b><tt>.</tt></b> or <b><tt>/</tt></b> on Unix-like
! platforms): create the file in the standard temporary directory, but use the
<b><tt><prefix_string></tt></b> as the first part of the file name.
E.g., if <b><tt><prefix_string></tt></b> is <b><tt>xx</tt></b>, the
--- 1044,1050 ----
<b><tt>/tmp/gs_a1234</tt></b>.
<p>
! <li>A string that contains only alphanumeric characters, underline,
! and dash: create the file in the standard temporary directory, but use
! the
<b><tt><prefix_string></tt></b> as the first part of the file name.
E.g., if <b><tt><prefix_string></tt></b> is <b><tt>xx</tt></b>, the
Index: src/gp.h
===================================================================
RCS file: /cvs/ghostscript/gs/src/gp.h,v
retrieving revision 1.24
diff -C2 -r1.24 gp.h
*** src/gp.h 8 Jul 2003 17:31:14 -0000 1.24
--- src/gp.h 14 Aug 2003 23:31:05 -0000
***************
*** 42,50 ****
*/
#include "srdline.h"
- /*
- * The definition for gp_file_name_combine_result is in gpmisc.h,
- * since it is shared with gpmisc.c .
- */
- #include "gpmisc.h"
/* ------ Initialization/termination ------ */
--- 42,45 ----
***************
*** 175,180 ****
extern const char gp_fmode_wb[];
! /* Create and open a scratch file with a given name prefix. */
! /* Write the actual file name at fname. */
FILE *gp_open_scratch_file(const char *prefix,
char fname[gp_file_name_sizeof],
--- 170,189 ----
extern const char gp_fmode_wb[];
! /**
! * gp_open_scratch_file: Create a scratch file.
! * @prefix: Name prefix.
! * @fname: Where to store filename of newly created file.
! * @mode: File access mode (in fopen syntax).
! *
! * Creates a scratch (temporary) file in the filesystem. The exact
! * location and name of the file is platform dependent, but in general
! * uses @prefix as a prefix. If @prefix is not absolute, then choose
! * an appropriate system directory, usually as determined from
! * gp_gettmpdir(), followed by a path as returned from a system call.
! *
! * Implementations should make sure that
! *
! * Return value: Opened file object, or NULL on error.
! **/
FILE *gp_open_scratch_file(const char *prefix,
char fname[gp_file_name_sizeof],
***************
*** 216,219 ****
--- 225,234 ----
const char *gp_file_name_concat_string(const char *prefix, uint plen);
#endif
+
+ typedef enum {
+ gp_combine_small_buffer = -1,
+ gp_combine_cant_handle = 0,
+ gp_combine_success = 1
+ } gp_file_name_combine_result;
/*
Index: src/gp_mswin.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gp_mswin.c,v
retrieving revision 1.21
diff -C2 -r1.21 gp_mswin.c
*** src/gp_mswin.c 8 Jul 2003 17:31:14 -0000 1.21
--- src/gp_mswin.c 14 Aug 2003 23:31:05 -0000
***************
*** 686,690 ****
int plen = sizeof(sTempDir);
! if (gp_gettmpdir(sTempDir, &plen) < 0)
l = GetTempPath(sizeof(sTempDir), sTempDir);
else
--- 686,690 ----
int plen = sizeof(sTempDir);
! if (gp_gettmpdir(sTempDir, &plen) != 0)
l = GetTempPath(sizeof(sTempDir), sTempDir);
else
Index: src/gp_vms.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gp_vms.c,v
retrieving revision 1.31
diff -C2 -r1.31 gp_vms.c
*** src/gp_vms.c 8 Jul 2003 17:31:14 -0000 1.31
--- src/gp_vms.c 14 Aug 2003 23:31:07 -0000
***************
*** 220,227 ****
{
FILE *f;
! if (strlen(prefix) + 6 >= gp_file_name_sizeof)
return 0; /* file name too long */
- strcpy(fname, prefix);
strcat(fname, "XXXXXX");
mktemp(fname);
--- 220,236 ----
{
FILE *f;
+ char tmpdir[gp_file_name_sizeof];
! if (!gp_file_name_is_absolute(prefix, strlen(prefix)) &&
! gp_gettmpdir(tmpdir, &tdlen) == 0) {
! if (gp_file_name_combine(tmpdir, tdlen, prefix, strlen(prefix),
! false, fname, gp_file_name_sizeof)) {
! return NULL;
! }
! } else {
! strcpy(fname, prefix);
! }
! if (strlen(fname) + 6 >= gp_file_name_sizeof)
return 0; /* file name too long */
strcat(fname, "XXXXXX");
mktemp(fname);
Index: src/gpmisc.h
===================================================================
RCS file: /cvs/ghostscript/gs/src/gpmisc.h,v
retrieving revision 1.11
diff -C2 -r1.11 gpmisc.h
*** src/gpmisc.h 12 Mar 2003 12:22:23 -0000 1.11
--- src/gpmisc.h 14 Aug 2003 23:31:07 -0000
***************
*** 39,48 ****
FILE *gp_fopentemp(const char *fname, const char *mode);
- typedef enum {
- gp_combine_small_buffer = -1,
- gp_combine_cant_handle = 0,
- gp_combine_success = 1
- } gp_file_name_combine_result;
-
/*
* Combine a file name with a prefix.
--- 39,42 ----
Index: src/lib.mak
===================================================================
RCS file: /cvs/ghostscript/gs/src/lib.mak,v
retrieving revision 1.130
diff -C2 -r1.130 lib.mak
*** src/lib.mak 1 Aug 2003 15:53:37 -0000 1.130
--- src/lib.mak 14 Aug 2003 23:31:08 -0000
***************
*** 62,66 ****
gpgetenv_h=$(GLSRC)gpgetenv.h
gpmisc_h=$(GLSRC)gpmisc.h
! gp_h=$(GLSRC)gp.h $(gpgetenv_h) $(gstypes_h) $(srdline_h) $(gpmisc_h)
gpcheck_h=$(GLSRC)gpcheck.h
gpsync_h=$(GLSRC)gpsync.h
--- 62,66 ----
gpgetenv_h=$(GLSRC)gpgetenv.h
gpmisc_h=$(GLSRC)gpmisc.h
! gp_h=$(GLSRC)gp.h $(gpgetenv_h) $(gstypes_h) $(srdline_h)
gpcheck_h=$(GLSRC)gpcheck.h
gpsync_h=$(GLSRC)gpsync.h
Index: src/zfile.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/zfile.c,v
retrieving revision 1.35
diff -C2 -r1.35 zfile.c
*** src/zfile.c 12 Aug 2003 13:32:22 -0000 1.35
--- src/zfile.c 14 Aug 2003 23:31:09 -0000
***************
*** 744,747 ****
--- 744,765 ----
}
+ /* A "simple" prefix is defined a nonempty string of alphanumeric,
+ underscore, and hyphen characters. */
+ private bool
+ prefix_is_simple(const char *pstr)
+ {
+ int i;
+ char c;
+
+ if (pstr[0] == 0) return false;
+
+ for (i = 0; (c = pstr[i]) != 0; i++) {
+ if (!(c == '-' || c == '_' || (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
+ return false;
+ }
+ return true;
+ }
+
/* <prefix|null> <access_string> .tempfile <name_string> <file> */
private int
***************
*** 758,765 ****
stream *s;
byte *buf;
- #if NEW_COMBINE_PATH
- char tdir[gp_file_name_sizeof];
- bool temp_dir = false;
- #endif
if (code < 0)
--- 776,779 ----
***************
*** 779,817 ****
pstr = prefix;
}
! #if NEW_COMBINE_PATH
! if (!gp_file_name_is_absolute(pstr, strlen(pstr))) {
! int tlen = sizeof(tdir);
! uint flen = sizeof(fname);
! gp_file_name_combine_result r;
!
! code = gp_gettmpdir(tdir, &tlen);
! if (code < 0 || tlen == 0 || tdir[0] == 0)
! strcpy(tdir, gp_file_name_current());
! else
! temp_dir = true;
! tlen = strlen(tdir);
! r = gp_file_name_combine_generic(tdir, tlen,
! pstr, strlen(pstr), i_ctx_p->LockFilePermissions, fname, &flen);
! if (r != gp_combine_success)
! return_error(e_undefinedfilename);
! memcpy(prefix, fname, flen);
! prefix[flen] = 0;
! pstr = prefix;
! }
! #endif
! if (i_ctx_p->LockFilePermissions)
! if (
! #if !NEW_COMBINE_PATH
! gp_file_name_references_parent(pstr, strlen(pstr)) ||
! (gp_pathstring_not_bare(pstr, strlen(pstr)) &&
! check_file_permissions(i_ctx_p, pstr, strlen(pstr),
! "PermitFileWriting") < 0 )
! #else
! !temp_dir &&
! check_file_permissions(i_ctx_p, pstr, strlen(pstr),
! "PermitFileWriting") < 0
! #endif
! )
return_error(e_invalidfileaccess);
s = file_alloc_stream(imemory, "ztempfile(stream)");
if (s == 0)
--- 793,806 ----
pstr = prefix;
}
!
! if (gp_file_name_is_absolute(pstr, strlen(pstr))) {
! if (check_file_permissions(i_ctx_p, pstr, strlen(pstr),
! "PermitFileWriting") < 0) {
return_error(e_invalidfileaccess);
+ }
+ } else if (!prefix_is_simple(pstr)) {
+ return_error(e_invalidfileaccess);
+ }
+
s = file_alloc_stream(imemory, "ztempfile(stream)");
if (s == 0)