Blame SOURCES/ghostscript-cve-2018-15908.patch

01c841
From: Chris Liddell <chris.liddell@artifex.com>
01c841
Date: Tue, 21 Aug 2018 19:17:05 +0000 (+0100)
01c841
Subject: Bug 699657: properly apply file permissions to .tempfile
01c841
01c841
Bug 699657: properly apply file permissions to .tempfile
01c841
01c841
https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=0d3901189f245232f0161addf215d7268c4d05a3
01c841
01c841
From: Chris Liddell <chris.liddell@artifex.com>
01c841
Date: Tue, 21 Aug 2018 19:17:51 +0000 (+0100)
01c841
Subject: Bug 699658: Fix handling of pre-SAFER opened files.
01c841
01c841
Bug 699658: Fix handling of pre-SAFER opened files.
01c841
01c841
Temp files opened for writing before SAFER is engaged are not subject to the
01c841
SAFER restrictions - that is handled by recording in a dictionary, and
01c841
checking that as part of the permissions checks.
01c841
01c841
By adding a custom error handler for invalidaccess, that allowed the filename
01c841
to be added to the dictionary (despite the attempted open throwing the error)
01c841
thus meaning subsequent accesses were erroneously permitted.
01c841
01c841
https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=a054156d425b4dbdaaa9fda4b5f1182b27598c2b
01c841
---
01c841
01c841
diff -up a/psi/zfile.c.cve-2018-15908 b/psi/zfile.c
01c841
--- a/psi/zfile.c.cve-2018-15908	2018-11-14 15:13:31.249625819 +0100
01c841
+++ b/psi/zfile.c	2018-11-14 15:14:16.933831779 +0100
01c841
@@ -121,7 +121,7 @@ make_invalid_file(i_ctx_t *i_ctx_p, ref
01c841
 /* strings of the permitgroup array. */
01c841
 static int
01c841
 check_file_permissions_reduced(i_ctx_t *i_ctx_p, const char *fname, int len,
01c841
-                        const char *permitgroup)
01c841
+                        gx_io_device *iodev, const char *permitgroup)
01c841
 {
01c841
     long i;
01c841
     ref *permitlist = NULL;
01c841
@@ -131,8 +131,14 @@ check_file_permissions_reduced(i_ctx_t *
01c841
     bool use_windows_pathsep = (gs_file_name_check_separator(win_sep2, 1, win_sep2) == 1);
01c841
     uint plen = gp_file_name_parents(fname, len);
01c841
 
01c841
-    /* Assuming a reduced file name. */
01c841
+    /* we're protecting arbitrary file system accesses, not Postscript device accesses.
01c841
+     * Although, note that %pipe% is explicitly checked for and disallowed elsewhere
01c841
+     */
01c841
+    if (iodev && iodev != iodev_default(imemory)) {
01c841
+        return 0;
01c841
+    }
01c841
 
01c841
+    /* Assuming a reduced file name. */
01c841
     if (dict_find_string(&(i_ctx_p->userparams), permitgroup, &permitlist) <= 0)
01c841
         return 0;       /* if Permissions not found, just allow access */
01c841
 
01c841
@@ -187,14 +193,14 @@ check_file_permissions_reduced(i_ctx_t *
01c841
 /* strings of the permitgroup array */
01c841
 static int
01c841
 check_file_permissions(i_ctx_t *i_ctx_p, const char *fname, int len,
01c841
-                        const char *permitgroup)
01c841
+                        gx_io_device *iodev, const char *permitgroup)
01c841
 {
01c841
     char fname_reduced[gp_file_name_sizeof];
01c841
     uint rlen = sizeof(fname_reduced);
01c841
 
01c841
     if (gp_file_name_reduce(fname, len, fname_reduced, &rlen) != gp_combine_success)
01c841
         return e_invalidaccess;         /* fail if we couldn't reduce */
01c841
-    return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, permitgroup);
01c841
+    return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, iodev, permitgroup);
01c841
 }
01c841
 
01c841
 /* <name_string> <access_string> file <file> */
01c841
@@ -298,7 +304,7 @@ zdeletefile(i_ctx_t *i_ctx_p)
01c841
         return code;
01c841
     if (pname.iodev == iodev_default(imemory)) {
01c841
         if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len,
01c841
-                "PermitFileControl")) < 0 &&
01c841
+                pname.iodev, "PermitFileControl")) < 0 &&
01c841
                  !file_is_tempfile(i_ctx_p, op->value.bytes, r_size(op))) {
01c841
             return code;
01c841
         }
01c841
@@ -382,7 +388,7 @@ file_continue(i_ctx_t *i_ctx_p)
01c841
         } else if (code > len)      /* overran string */
01c841
             return_error(gs_error_rangecheck);
01c841
         else if (iodev != iodev_default(imemory)
01c841
-              || (check_file_permissions_reduced(i_ctx_p, (char *)pscratch->value.bytes, code + devlen, "PermitFileReading")) == 0) {
01c841
+              || (check_file_permissions_reduced(i_ctx_p, (char *)pscratch->value.bytes, code + devlen, NULL, "PermitFileReading")) == 0) {
01c841
             push(1);
01c841
             ref_assign(op, pscratch);
01c841
             r_set_size(op, code + devlen);
01c841
@@ -432,12 +438,12 @@ zrenamefile(i_ctx_t *i_ctx_p)
01c841
                  * and FileWriting permissions to the destination file/path.
01c841
                  */
01c841
               ((check_file_permissions(i_ctx_p, pname1.fname, pname1.len,
01c841
-                                        "PermitFileControl") < 0 &&
01c841
+                                        pname1.iodev, "PermitFileControl") < 0 &&
01c841
                   !file_is_tempfile(i_ctx_p, op[-1].value.bytes, r_size(op - 1))) ||
01c841
               (check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
01c841
-                                        "PermitFileControl") < 0 ||
01c841
+                                        pname2.iodev, "PermitFileControl") < 0 ||
01c841
               check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
01c841
-                                        "PermitFileWriting") < 0 )))) {
01c841
+                                        pname2.iodev, "PermitFileWriting") < 0 )))) {
01c841
             code = gs_note_error(e_invalidfileaccess);
01c841
         } else {
01c841
             code = (*pname1.iodev->procs.rename_file)(pname1.iodev,
01c841
@@ -484,8 +490,11 @@ zstatus(i_ctx_t *i_ctx_p)
01c841
                 code = gs_terminate_file_name(&pname, imemory, "status");
01c841
                 if (code < 0)
01c841
                     return code;
01c841
-                code = (*pname.iodev->procs.file_status)(pname.iodev,
01c841
+                if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len,
01c841
+                                       pname.iodev, "PermitFileReading")) >= 0) {
01c841
+                    code = (*pname.iodev->procs.file_status)(pname.iodev,
01c841
                                                        pname.fname, &fstat);
01c841
+                }
01c841
                 switch (code) {
01c841
                     case 0:
01c841
                         check_ostack(4);
01c841
@@ -694,8 +703,24 @@ ztempfile(i_ctx_t *i_ctx_p)
01c841
     }
01c841
 
01c841
     if (gp_file_name_is_absolute(pstr, strlen(pstr))) {
01c841
-        if (check_file_permissions(i_ctx_p, pstr, strlen(pstr),
01c841
-                                   "PermitFileWriting") < 0) {
01c841
+        int plen = strlen(pstr);
01c841
+        const char *sep = gp_file_name_separator();
01c841
+#ifdef DEBUG
01c841
+        int seplen = strlen(sep);
01c841
+        if (seplen != 1)
01c841
+            return_error(gs_error_Fatal);
01c841
+#endif
01c841
+        /* strip off the file name prefix, leave just the directory name
01c841
+         * so we can check if we are allowed to write to it
01c841
+         */
01c841
+        for ( ; plen >=0; plen--) {
01c841
+            if (pstr[plen] == sep[0])
01c841
+                break;
01c841
+        }
01c841
+        memcpy(fname, pstr, plen);
01c841
+        fname[plen] = '\0';
01c841
+        if (check_file_permissions(i_ctx_p, fname, strlen(fname),
01c841
+                                   NULL, "PermitFileWriting") < 0) {
01c841
             return_error(e_invalidfileaccess);
01c841
         }
01c841
     } else if (!prefix_is_simple(pstr)) {
01c841
@@ -837,6 +862,7 @@ zopen_file(i_ctx_t *i_ctx_p, const gs_pa
01c841
            const char *file_access, stream **ps, gs_memory_t *mem)
01c841
 {
01c841
     gx_io_device *const iodev = pfn->iodev;
01c841
+    int code = 0;
01c841
 
01c841
     if (pfn->fname == NULL)     /* just a device */
01c841
         return iodev->procs.open_device(iodev, file_access, ps, mem);
01c841
@@ -847,7 +873,7 @@ zopen_file(i_ctx_t *i_ctx_p, const gs_pa
01c841
             open_file = iodev_os_open_file;
01c841
         /* Check OS files to make sure we allow the type of access */
01c841
         if (open_file == iodev_os_open_file) {
01c841
-            int code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len,
01c841
+            code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len, pfn->iodev,
01c841
                 file_access[0] == 'r' ? "PermitFileReading" : "PermitFileWriting");
01c841
 
01c841
             if (code < 0 && !file_is_tempfile(i_ctx_p,
01c841
@@ -894,7 +920,7 @@ check_file_permissions_aux(i_ctx_t *i_ct
01c841
     /* fname must be reduced. */
01c841
     if (i_ctx_p == NULL)
01c841
         return 0;
01c841
-    if (check_file_permissions_reduced(i_ctx_p, fname, flen, "PermitFileReading") < 0)
01c841
+    if (check_file_permissions_reduced(i_ctx_p, fname, flen, NULL, "PermitFileReading") < 0)
01c841
         return_error(e_invalidfileaccess);
01c841
     return 0;
01c841
 }
01c841
diff -up a/Resource/Init/gs_init.ps.cve-2018-15908 b/Resource/Init/gs_init.ps
01c841
--- a/Resource/Init/gs_init.ps.cve-2018-15908	2018-11-14 16:34:23.268867657 +0100
01c841
+++ b/Resource/Init/gs_init.ps	2018-11-14 16:36:38.765552576 +0100
01c841
@@ -2015,6 +2015,19 @@ readonly def
01c841
             concatstrings concatstrings .generate_dir_list_templates
01c841
         } if
01c841
       ]
01c841
+      /PermitFileWriting [
01c841
+          currentuserparams /PermitFileWriting get aload pop
01c841
+          (TMPDIR) getenv not
01c841
+          {
01c841
+            (TEMP) getenv not
01c841
+            {
01c841
+              (TMP) getenv not
01c841
+              {
01c841
+                (/temp) (/tmp)
01c841
+              } if
01c841
+            } if
01c841
+          } if
01c841
+      ]
01c841
       /LockFilePermissions //true
01c841
     >> setuserparams
01c841
   }
01c841
@@ -2062,7 +2075,9 @@ readonly def
01c841
 % the file can be deleted later, even if SAFER is set.
01c841
 /.tempfile {
01c841
   .tempfile	% filename file
01c841
-  //SAFETY /tempfiles get 2 .argindex //true .forceput
01c841
+    //SAFETY /safe get not { % only add the filename if we're not yet safe
01c841
+    //SAFETY /tempfiles get 2 .argindex //true .forceput
01c841
+  } if
01c841
 } .bind executeonly odef
01c841
 
01c841
 % If we are running in SAFER mode, lock things down