From 835e373b3eeaabcd0621ed6798ab500f37982fae Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Wed, 5 Apr 2023 14:13:39 -0400 Subject: xpdf-no-select-disable --- xpdf/SecurityHandler.cc | 370 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 xpdf/SecurityHandler.cc (limited to 'xpdf/SecurityHandler.cc') diff --git a/xpdf/SecurityHandler.cc b/xpdf/SecurityHandler.cc new file mode 100644 index 0000000..a61a84e --- /dev/null +++ b/xpdf/SecurityHandler.cc @@ -0,0 +1,370 @@ +//======================================================================== +// +// SecurityHandler.cc +// +// Copyright 2004 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "gmempp.h" +#include "GString.h" +#include "PDFDoc.h" +#include "Decrypt.h" +#include "Error.h" +#include "GlobalParams.h" +#include "PDFCore.h" +#include "SecurityHandler.h" + +//------------------------------------------------------------------------ +// SecurityHandler +//------------------------------------------------------------------------ + +SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { + Object filterObj; + SecurityHandler *secHdlr; + + encryptDictA->dictLookup("Filter", &filterObj); + if (filterObj.isName("Standard")) { + secHdlr = new StandardSecurityHandler(docA, encryptDictA); + } else if (filterObj.isName()) { + error(errSyntaxError, -1, "Couldn't find the '{0:s}' security handler", + filterObj.getName()); + secHdlr = NULL; + } else { + error(errSyntaxError, -1, + "Missing or invalid 'Filter' entry in encryption dictionary"); + secHdlr = NULL; + } + filterObj.free(); + return secHdlr; +} + +SecurityHandler::SecurityHandler(PDFDoc *docA) { + doc = docA; +} + +SecurityHandler::~SecurityHandler() { +} + +GBool SecurityHandler::checkEncryption(GString *ownerPassword, + GString *userPassword) { + void *authData; + GBool ok; + int i; + + if (ownerPassword || userPassword) { + authData = makeAuthData(ownerPassword, userPassword); + } else { + authData = NULL; + } + ok = authorize(authData); + if (authData) { + freeAuthData(authData); + } + for (i = 0; !ok && i < 3; ++i) { + if (!(authData = getAuthData())) { + break; + } + ok = authorize(authData); + if (authData) { + freeAuthData(authData); + } + } + if (!ok) { + error(errCommandLine, -1, "Incorrect password"); + } + return ok; +} + +//------------------------------------------------------------------------ +// StandardSecurityHandler +//------------------------------------------------------------------------ + +class StandardAuthData { +public: + + StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) { + ownerPassword = ownerPasswordA; + userPassword = userPasswordA; + } + + ~StandardAuthData() { + if (ownerPassword) { + delete ownerPassword; + } + if (userPassword) { + delete userPassword; + } + } + + GString *ownerPassword; + GString *userPassword; +}; + +StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, + Object *encryptDictA): + SecurityHandler(docA) +{ + Object versionObj, revisionObj, lengthObj; + Object ownerKeyObj, userKeyObj, ownerEncObj, userEncObj; + Object permObj, fileIDObj, fileIDObj1; + Object cryptFiltersObj, streamFilterObj, stringFilterObj; + Object cryptFilterObj, cfmObj, cfLengthObj; + Object encryptMetadataObj; + + ok = gFalse; + fileID = NULL; + ownerKey = NULL; + userKey = NULL; + ownerEnc = NULL; + userEnc = NULL; + fileKeyLength = 0; + + //--- get the main parameters + encryptDictA->dictLookup("V", &versionObj); + encryptDictA->dictLookup("R", &revisionObj); + encryptDictA->dictLookup("Length", &lengthObj); + encryptDictA->dictLookup("O", &ownerKeyObj); + encryptDictA->dictLookup("U", &userKeyObj); + encryptDictA->dictLookup("OE", &ownerEncObj); + encryptDictA->dictLookup("UE", &userEncObj); + encryptDictA->dictLookup("P", &permObj); + doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj); + if (!versionObj.isInt() || + !revisionObj.isInt() || + !permObj.isInt() || + !ownerKeyObj.isString() || + !userKeyObj.isString()) { + error(errSyntaxError, -1, "Invalid encryption parameters"); + goto done; + } + encVersion = versionObj.getInt(); + encRevision = revisionObj.getInt(); + encAlgorithm = cryptRC4; + // revision 2 forces a 40-bit key - some buggy PDF generators + // set the Length value incorrectly + if (encRevision == 2 || !lengthObj.isInt()) { + fileKeyLength = 5; + } else { + fileKeyLength = lengthObj.getInt() / 8; + } + encryptMetadata = gTrue; + + //--- check for a crypt filter (which can modify the parameters) + //~ this currently only handles a subset of crypt filter functionality + //~ (in particular, it ignores the EFF entry in encryptDictA, and + //~ doesn't handle the case where StmF, StrF, and EFF are not all the + //~ same) + if ((encVersion == 4 || encVersion == 5) && + (encRevision == 4 || encRevision == 5 || encRevision == 6)) { + encryptDictA->dictLookup("CF", &cryptFiltersObj); + encryptDictA->dictLookup("StmF", &streamFilterObj); + encryptDictA->dictLookup("StrF", &stringFilterObj); + if (cryptFiltersObj.isDict() && + streamFilterObj.isName() && + stringFilterObj.isName() && + !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { + if (!strcmp(streamFilterObj.getName(), "Identity")) { + // no encryption on streams or strings + stringFilterObj.free(); + streamFilterObj.free(); + cryptFiltersObj.free(); + goto done; + } + if (cryptFiltersObj.dictLookup(streamFilterObj.getName(), + &cryptFilterObj)->isDict()) { + cryptFilterObj.dictLookup("CFM", &cfmObj); + if (cfmObj.isName("V2")) { + if (cryptFilterObj.dictLookup("Length", + &cfLengthObj)->isInt()) { + fileKeyLength = cfLengthObj.getInt(); + } + cfLengthObj.free(); + encVersion = 2; + encRevision = 3; + } else if (cfmObj.isName("AESV2")) { + if (cryptFilterObj.dictLookup("Length", + &cfLengthObj)->isInt()) { + fileKeyLength = cfLengthObj.getInt(); + } + cfLengthObj.free(); + encVersion = 2; + encRevision = 3; + encAlgorithm = cryptAES; + } else if (cfmObj.isName("AESV3")) { + if (cryptFilterObj.dictLookup("Length", + &cfLengthObj)->isInt()) { + fileKeyLength = cfLengthObj.getInt(); + } + cfLengthObj.free(); + encVersion = 5; + if (encRevision != 5 && encRevision != 6) { + encRevision = 6; + } + encAlgorithm = cryptAES256; + // The PDF 2.0 spec says Length and CF.Length are both deprecated. + // Acrobat X honors Length and ignores CF.Length. + // I think it's safest to ignore both. + fileKeyLength = 32; + } + cfmObj.free(); + } + cryptFilterObj.free(); + } + stringFilterObj.free(); + streamFilterObj.free(); + cryptFiltersObj.free(); + if (encryptDictA->dictLookup("EncryptMetadata", + &encryptMetadataObj)->isBool()) { + encryptMetadata = encryptMetadataObj.getBool(); + } + encryptMetadataObj.free(); + } + + //--- version-specific parameters + if (encRevision <= 4) { + if (ownerKeyObj.getString()->getLength() != 32 || + userKeyObj.getString()->getLength() != 32) { + error(errSyntaxError, -1, "Invalid encryption key length"); + // this is non-fatal -- see below + } + } else if (encRevision <= 6) { + // the spec says 48 bytes, but Acrobat pads them out longer + if (ownerKeyObj.getString()->getLength() < 48 || + userKeyObj.getString()->getLength() < 48 || + !ownerEncObj.isString() || + ownerEncObj.getString()->getLength() != 32 || + !userEncObj.isString() || + userEncObj.getString()->getLength() != 32) { + error(errSyntaxError, -1, "Invalid encryption key length"); + goto done; + } + } + permFlags = permObj.getInt(); + ownerKey = ownerKeyObj.getString()->copy(); + userKey = userKeyObj.getString()->copy(); + if (encRevision <= 4) { + // Adobe apparently zero-pads the U value (and maybe the O value?) + // if it's short + while (ownerKey->getLength() < 32) { + ownerKey->append((char)0x00); + } + while (userKey->getLength() < 32) { + userKey->append((char)0x00); + } + } + if (encVersion >= 1 && encVersion <= 2 && + encRevision >= 2 && encRevision <= 3) { + if (fileIDObj.isArray()) { + if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) { + fileID = fileIDObj1.getString()->copy(); + } else { + fileID = new GString(); + } + fileIDObj1.free(); + } else { + fileID = new GString(); + } + if (fileKeyLength > 16 || fileKeyLength <= 0) { + fileKeyLength = 16; + } + ok = gTrue; + } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) { + fileID = new GString(); // unused for V=R=5 + ownerEnc = ownerEncObj.getString()->copy(); + userEnc = userEncObj.getString()->copy(); + if (fileKeyLength > 32 || fileKeyLength <= 0) { + fileKeyLength = 32; + } + ok = gTrue; + } else { + error(errUnimplemented, -1, + "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler", + encVersion, encRevision); + } + + done: + fileIDObj.free(); + permObj.free(); + userEncObj.free(); + ownerEncObj.free(); + userKeyObj.free(); + ownerKeyObj.free(); + lengthObj.free(); + revisionObj.free(); + versionObj.free(); +} + +StandardSecurityHandler::~StandardSecurityHandler() { + if (fileID) { + delete fileID; + } + if (ownerKey) { + delete ownerKey; + } + if (userKey) { + delete userKey; + } + if (ownerEnc) { + delete ownerEnc; + } + if (userEnc) { + delete userEnc; + } +} + +GBool StandardSecurityHandler::isUnencrypted() { + return encVersion == -1 && encRevision == -1; +} + +void *StandardSecurityHandler::makeAuthData(GString *ownerPassword, + GString *userPassword) { + return new StandardAuthData(ownerPassword ? ownerPassword->copy() + : (GString *)NULL, + userPassword ? userPassword->copy() + : (GString *)NULL); +} + +void *StandardSecurityHandler::getAuthData() { + PDFCore *core; + GString *password; + + if (!(core = doc->getCore()) || + !(password = core->getPassword())) { + return NULL; + } + return new StandardAuthData(password, password->copy()); +} + +void StandardSecurityHandler::freeAuthData(void *authData) { + delete (StandardAuthData *)authData; +} + +GBool StandardSecurityHandler::authorize(void *authData) { + GString *ownerPassword, *userPassword; + + if (!ok) { + return gFalse; + } + if (authData) { + ownerPassword = ((StandardAuthData *)authData)->ownerPassword; + userPassword = ((StandardAuthData *)authData)->userPassword; + } else { + ownerPassword = NULL; + userPassword = NULL; + } + if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, + ownerKey, userKey, ownerEnc, userEnc, + permFlags, fileID, + ownerPassword, userPassword, fileKey, + encryptMetadata, &ownerPasswordOk)) { + return gFalse; + } + return gTrue; +} -- cgit v1.2.3