aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-01-18 01:44:18 +0100
committerJay Berkenbilt <ejb@ql.org>2019-01-19 16:10:58 +0100
commit392f2ece51e66dd4c92df3be7f91b637cb54c059 (patch)
treec9dbbe5a6b65bb4bbda113b658d60a7022251c18
parente4fa5a3c2a90be455e04a8e4d5b9257a1ba92883 (diff)
downloadqpdf-392f2ece51e66dd4c92df3be7f91b637cb54c059.tar.zst
Try passwords with different string encodings
-rw-r--r--ChangeLog7
-rw-r--r--qpdf/qpdf.cc79
2 files changed, 85 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 992cf507..4c1e62d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2019-01-17 Jay Berkenbilt <ejb@ql.org>
+ * When attempting to open an encrypted file with a password, if
+ the password doesn't work, try alternative passwords created by
+ re-interpreting the supplied password with different string
+ encodings. This makes qpdf able to recover passwords with
+ non-ASCII characters when either the decryption or encryption
+ operation was performed with an incorrectly encoded password.
+
* Fix data loss bug: qpdf was discarding referenced resources in
the case in which a page's resource dictionary contained an
indirect reference for either /Font or /XObject that contained
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index 9ee2b423..999541fb 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -3671,7 +3671,7 @@ ImageOptimizer::provideStreamData(int, int, Pipeline* pipeline)
}
template <typename T>
-static PointerHolder<QPDF> do_process(
+static PointerHolder<QPDF> do_process_once(
void (QPDF::*fn)(T, char const*),
T item, char const* password,
Options& o, bool empty)
@@ -3689,6 +3689,83 @@ static PointerHolder<QPDF> do_process(
return pdf;
}
+template <typename T>
+static PointerHolder<QPDF> do_process(
+ void (QPDF::*fn)(T, char const*),
+ T item, char const* password,
+ Options& o, bool empty)
+{
+ // If a password has been specified but doesn't work, try other
+ // passwords that are equivalent in different character encodings.
+ // This makes it possible to open PDF files that were encrypted
+ // using incorrect string encodings. For example, if someone used
+ // a password encoded in PDF Doc encoding or Windows code page
+ // 1252 for an AES-encrypted file or a UTF-8-encoded password on
+ // an RC4-encrypted file, or if the password was properly encoded
+ // by the password given here was incorrectly encoded, there's a
+ // good chance we'd succeed here.
+
+ if ((password == 0) || empty || o.password_is_hex_key)
+ {
+ // There is no password, so just do the normal processing.
+ return do_process_once(fn, item, password, o, empty);
+ }
+
+ // Get a list of otherwise encoded strings. Keep in scope for this
+ // method.
+ std::vector<std::string> passwords_str =
+ QUtil::possible_repaired_encodings(password);
+ // Represent to char const*, as required by the QPDF class.
+ std::vector<char const*> passwords;
+ for (std::vector<std::string>::iterator iter = passwords_str.begin();
+ iter != passwords_str.end(); ++iter)
+ {
+ passwords.push_back((*iter).c_str());
+ }
+ // We always try the supplied password first because it is the
+ // first string returned by possible_repaired_encodings. If there
+ // is more than one option, go ahead and put the supplied password
+ // at the end so that it's that decoding attempt whose exception
+ // is thrown.
+ if (passwords.size() > 1)
+ {
+ passwords.push_back(password);
+ }
+
+ // Try each password. If one works, return the resulting object.
+ // If they all fail, throw the exception thrown by the final
+ // attempt, which, like the first attempt, will be with the
+ // supplied password.
+ bool warned = false;
+ for (std::vector<char const*>::iterator iter = passwords.begin();
+ iter != passwords.end(); ++iter)
+ {
+ try
+ {
+ return do_process_once(fn, item, *iter, o, empty);
+ }
+ catch (QPDFExc& e)
+ {
+ std::vector<char const*>::iterator next = iter;
+ ++next;
+ if (next == passwords.end())
+ {
+ throw e;
+ }
+ }
+ if ((! warned) && o.verbose)
+ {
+ warned = true;
+ std::cout << whoami << ": supplied password didn't work;"
+ << " trying other passwords based on interpreting"
+ << " password with different string encodings"
+ << std::endl;
+ }
+ }
+ // Should not be reachable
+ throw std::logic_error("do_process returned");
+}
+
static PointerHolder<QPDF> process_file(char const* filename,
char const* password,
Options& o)