Bug 706864: Cache non-decrypted encryption dict/ID after repair.
authorSebastian Rasmussen <[email protected]>
Wed, 16 Aug 2023 22:38:05 +0000 (00:38 +0200)
committerSebastian Rasmussen <[email protected]>
Fri, 25 Aug 2023 14:55:41 +0000 (16:55 +0200)
When opening a document a non-decrypted encryption dict/ID are read, then
the rest of the document. If a repair is triggered it used to throw away
all cached objects to avoid caching non-decrypted strings/streams.
This is the correct decision of all objects except the encryption dict/ID.
If at a later point the PDF document is saved with encryption kept then
all objects will be saved using the encryption key from the non-decrypted
encryption dictionary/ID determined when opening the document, but when
saving the encryption dictionary itself it will not get any special
treatment, thus its owner/user password strings are decrypted. This
decrypted encryption dict and ID are then saved to the output file.
This means that the owner/user password strings no longer correspond
to the ones in the original document. So when mupdf-gl tries to reopen
the output file it will ask for a password, but the original owner/user
password's will not work.

This is fixed by caching the encryption dictionary and ID in non-decrypted
form after repairing the document and clearing out all the non-decrypted
objects. Doing that leaves the objects in memory until it is time to save
the document, causing the encryption dictionary to be saved in non-decrypted
form, which means that the owner/user password strings are left unchanged,
and thus mupdf-gl will accept the original owner/user passwords when reopening
the file.

For the file in this particular bug the owner password is unknown while the
user password is the default password (i.e. the empty string).

source/pdf/pdf-xref.c

index 7d507c39a2cb5444c901a44ab79b1c0b53551d0a..19ce9cfd53acfcc5fab908d2c342ea0681d3280a 100644 (file)
@@ -1927,7 +1927,28 @@ void pdf_repair_trailer(fz_context *ctx, pdf_document *doc)
        {
                /* ensure that strings are not used in their repaired, non-decrypted form */
                if (doc->crypt)
+               {
+                       pdf_crypt *tmp;
                        pdf_clear_xref(ctx, doc);
+
+                       /* ensure that Encryption dictionary and ID are cached without decryption,
+                          otherwise a decrypted Encryption dictionary and ID may be used when saving
+                          the PDF causing it to be inconsistent (since strings/streams are encrypted
+                          with the actual encryption key, not the decrypted encryption key). */
+                       tmp = doc->crypt;
+                       doc->crypt = NULL;
+                       fz_try(ctx)
+                       {
+                               (void) pdf_resolve_indirect(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Encrypt)));
+                               (void) pdf_resolve_indirect(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(ID)));
+                       }
+                       fz_always(ctx)
+                               doc->crypt = tmp;
+                       fz_catch(ctx)
+                       {
+                               fz_rethrow(ctx);
+                       }
+               }
        }
        fz_catch(ctx)
        {