diff options
author | Jay Berkenbilt <ejb@ql.org> | 2019-01-18 01:44:18 +0100 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2019-01-19 16:10:58 +0100 |
commit | 392f2ece51e66dd4c92df3be7f91b637cb54c059 (patch) | |
tree | c9dbbe5a6b65bb4bbda113b658d60a7022251c18 /qpdf | |
parent | e4fa5a3c2a90be455e04a8e4d5b9257a1ba92883 (diff) | |
download | qpdf-392f2ece51e66dd4c92df3be7f91b637cb54c059.tar.zst |
Try passwords with different string encodings
Diffstat (limited to 'qpdf')
-rw-r--r-- | qpdf/qpdf.cc | 79 |
1 files changed, 78 insertions, 1 deletions
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) |