summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Astals Cid <aacid@kde.org>2019-12-07 00:18:53 +0100
committerAlbert Astals Cid <tsdgeos@yahoo.es>2020-02-20 21:18:50 +0000
commitddf97254576a88bd85fcc7b41876c90d1780504e (patch)
tree219b152ed3cd4556eade31de0db450f4c01f8c94
parentbdafd6066df79e8183ee311fb086993c480b2c55 (diff)
Make sure Base URI is encrypted if the document is before using it
Otherwise we may be being targetted by a link content exfiltration
-rw-r--r--poppler/Catalog.cc4
-rw-r--r--poppler/Dict.cc17
-rw-r--r--poppler/Dict.h3
-rw-r--r--poppler/Stream.cc8
-rw-r--r--poppler/Stream.h7
-rw-r--r--poppler/XRef.cc29
-rw-r--r--poppler/XRef.h4
7 files changed, 68 insertions, 4 deletions
diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index f1afc6e5..05498c3b 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -102,9 +102,9 @@ Catalog::Catalog(PDFDoc *docA) {
acroForm = catDict.dictLookup("AcroForm");
// read base URI
- Object obj = catDict.dictLookup("URI");
+ Object obj = catDict.getDict()->lookupEnsureEncryptedIfNeeded("URI");
if (obj.isDict()) {
- Object obj2 = obj.dictLookup("Base");
+ Object obj2 = obj.getDict()->lookupEnsureEncryptedIfNeeded("Base");
if (obj2.isString()) {
baseURI = obj2.getString()->copy();
}
diff --git a/poppler/Dict.cc b/poppler/Dict.cc
index 77b8ee18..61b89c2e 100644
--- a/poppler/Dict.cc
+++ b/poppler/Dict.cc
@@ -180,6 +180,23 @@ Object Dict::lookup(const char *key, Ref *returnRef, int recursion) const {
return Object(objNull);
}
+Object Dict::lookupEnsureEncryptedIfNeeded(const char *key) const
+{
+ const auto *entry = find(key);
+ if (!entry)
+ return Object(objNull);
+
+ if (entry->second.getType() == objRef &&
+ xref->isEncrypted() &&
+ !xref->isRefEncrypted(entry->second.getRef()))
+ {
+ error(errSyntaxError, -1, "{0:s} is not encrypted and the document is. This may be a hacking attempt", key);
+ return Object(objNull);
+ }
+
+ return entry->second.fetch(xref);
+}
+
const Object &Dict::lookupNF(const char *key) const {
if (const auto *entry = find(key)) {
return entry->second;
diff --git a/poppler/Dict.h b/poppler/Dict.h
index 287d5a3c..140be8b2 100644
--- a/poppler/Dict.h
+++ b/poppler/Dict.h
@@ -78,6 +78,9 @@ public:
Object lookup(const char *key, int recursion = 0) const;
// Same as above but if the returned object is a fetched Ref returns such Ref in returnRef, otherwise returnRef is Ref::INVALID()
Object lookup(const char *key, Ref *returnRef, int recursion = 0) const;
+ // Look up an entry and return the value. Returns a null object
+ // if <key> is not in the dictionary or if it is a ref to a non encrypted object in a partially encrypted document
+ Object lookupEnsureEncryptedIfNeeded(const char *key) const;
const Object &lookupNF(const char *key) const;
bool lookupInt(const char *key, const char *alt_key, int *value) const;
diff --git a/poppler/Stream.cc b/poppler/Stream.cc
index fa89fc5c..59db9d07 100644
--- a/poppler/Stream.cc
+++ b/poppler/Stream.cc
@@ -176,6 +176,14 @@ Stream *Stream::addFilters(Dict *dict, int recursion) {
return str;
}
+bool Stream::isEncrypted() const {
+ for (const Stream *str = this; str != nullptr; str = str->getNextStream()) {
+ if (str->getKind() == strCrypt)
+ return true;
+ }
+ return false;
+}
+
class BaseStreamStream : public Stream
{
public:
diff --git a/poppler/Stream.h b/poppler/Stream.h
index b675bcc6..406840e6 100644
--- a/poppler/Stream.h
+++ b/poppler/Stream.h
@@ -220,12 +220,15 @@ public:
StreamColorSpaceMode * /*csMode*/) {}
// Return the next stream in the "stack".
- virtual Stream *getNextStream() { return nullptr; }
+ virtual Stream *getNextStream() const { return nullptr; }
// Add filters to this stream according to the parameters in <dict>.
// Returns the new stream.
Stream *addFilters(Dict *dict, int recursion = 0);
+ // Returns true if this stream includes a crypt filter.
+ bool isEncrypted() const;
+
private:
friend class Object; // for incRef/decRef
@@ -402,7 +405,7 @@ public:
Stream *getUndecodedStream() override { return str->getUndecodedStream(); }
Dict *getDict() override { return str->getDict(); }
Object *getDictObject() override { return str->getDictObject(); }
- Stream *getNextStream() override { return str; }
+ Stream *getNextStream() const override { return str; }
int getUnfilteredChar () override { return str->getUnfilteredChar(); }
void unfilteredReset () override { str->unfilteredReset(); }
diff --git a/poppler/XRef.cc b/poppler/XRef.cc
index 8f9dd7fe..b123ad23 100644
--- a/poppler/XRef.cc
+++ b/poppler/XRef.cc
@@ -1012,6 +1012,35 @@ void XRef::getEncryptionParameters(unsigned char **fileKeyA, CryptAlgorithm *enc
}
}
+bool XRef::isRefEncrypted(Ref r)
+{
+ xrefLocker();
+
+ const XRefEntry *e = getEntry(r.num);
+ if (!e->obj.isNull()) { //check for updated object
+ return false;
+ }
+
+ switch (e->type) {
+ case xrefEntryUncompressed:
+ {
+ return encrypted && !e->getFlag(XRefEntry::Unencrypted);
+ }
+
+ case xrefEntryCompressed:
+ {
+ const Object objStr = fetch(e->offset, 0);
+ return objStr.getStream()->isEncrypted();
+ }
+
+ default:
+ {
+ }
+ }
+
+ return false;
+}
+
bool XRef::okToPrint(bool ignoreOwnerPW) const {
return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
}
diff --git a/poppler/XRef.h b/poppler/XRef.h
index 2e72ae54..8950980b 100644
--- a/poppler/XRef.h
+++ b/poppler/XRef.h
@@ -128,6 +128,9 @@ public:
// Is the file encrypted?
bool isEncrypted() const { return encrypted; }
+ // Is the given Ref encrypted?
+ bool isRefEncrypted(Ref r);
+
// Check various permissions.
bool okToPrint(bool ignoreOwnerPW = false) const;
bool okToPrintHighRes(bool ignoreOwnerPW = false) const;
@@ -163,6 +166,7 @@ public:
// Return the catalog object reference.
int getRootNum() const { return rootNum; }
int getRootGen() const { return rootGen; }
+ Ref getRoot() const { return { rootNum, rootGen }; }
// Get end position for a stream in a damaged file.
// Returns false if unknown or file is not damaged.